Почему params ведет себя так?
Выход
1
2
ноль
2
Код
class Program
{
static void Main(String[] args)
{
String s = null;
PrintLength(s);
PrintLength(s, s);
PrintLength(null);
PrintLength(null, null);
Console.ReadKey();
}
private static void PrintLength(params String[] items)
{
Console.WriteLine(items == null ? "null" : items.Length.ToString());
}
}
4 ответа
Это довольно часто задаваемый вопрос. Подробнее см. В разделах 7.4.1 и 7.4.3.1 спецификации.
Вкратце: метод с массивом params применим либо в "нормальной форме", либо в "расширенной форме". То есть можно сказать
PrintLength(new string[] {"hello"}); // normal form
PrintLength("hello"); // expanded form, translated into normal form by compiler.
При получении вызова, который применим в обеих формах, компилятор всегда выбирает нормальную форму поверх расширенной формы.
Предположим, мы выбрали расширенную форму каждый раз, когда оба были применимы. Предположим, вы имели
void M(params object[] x) {}
Как бы вы на самом деле передали нулевой массив этой вещи, если бы мы всегда выбирали расширенную форму? Это было бы невозможно!
Предположим, вы сказали
M(new object[] { "hello" });
и мы всегда выбирали расширенную форму. Что бы это сделать? Ну, массив объектов - это объект, поэтому он выбрал бы расширенную форму - он создал бы другой массив, обернул бы эту вещь в массив и передал бы это!
Выбор расширенной формы по сравнению с обычной формой приводит к сумасшедшим результатам. Всегда разумнее выбирать нормальную форму, а не расширенную.
Он работает как задумано, я бы сказал:
PrintLength(ы);
Вы передаете одну строку, которая является нулевой - внутри вашего метода, items
не будет нулевым - это массив из одного элемента - типа строка - значения null
PrintLength(s, s);
Та же история здесь - вы проходите в два элемента, так items
в вашем методе будет массив из двух строк - обе из которых являются нулевыми сами по себе, но массив не
PrintLength(нуль);
Это, очевидно, интерпретируется как одно значение NULL и, таким образом, items
нулевой. Вы не передаете ни массив, ни элемент типа string - вы просто передаете нулевое значение как таковое.
PrintLength(ноль, ноль);
Это снова - массив из двух элементов, оба из которых являются нулевыми, но сам по себе массив не является нулевым, поскольку вы передаете два значения.
Возможно, это немного озадачивает, но на самом деле: что вы должны проверить в своем PrintLength
метод не является ли ваш Items
в целом является нулевым - но являются ли фактические значения items[0]
и так далее, являются нулевыми.
Что может быть немного странным - или поначалу нелогичным - это тот факт, что одно явное "нулевое" значение трактуется как "нулевое", а не как массив одного элемента со значением "ноль". Почему это так и могло ли это быть реализовано иначе - я не знаю, честно говоря.
PrintLength(null)
передает пустой массив, где как PrintLength(null, null)
проходит string[]
с длиной два, содержащий два нуля string
объекты. Это было бы так же, как прохождение new string[] { null, null }
Хм, читая то, что я написал, может быть, это на самом деле не отвечает на ваш вопрос.
Редактировать:
Вероятно, поэтому: вы можете отправить разделенный запятыми список аргументов типа, указанного в объявлении параметра, или массив аргументов указанного типа.
Как сказано в ответе один:
1: массив строк с одним элементом - элемент является нулевым
2: массив строк с двумя элементами, оба элемента равны нулю
null: в метод передается null, а не массив
2: массив нулей передается в метод