Почему 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());
}
}
Ответы
Ответ 1
Это довольно часто задаваемый вопрос. Подробности см. В разделах 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" });
и мы всегда выбираем расширенную форму. Что бы это сделало? Ну, массив объектов - это объект, поэтому он будет выбирать расширенную форму - он сделает другой массив, обернет это в массив и передаст это!
Выбор расширенной формы над нормальной формой приводит к сумасшедшим результатам. Всегда выбирать нормальную форму по расширенной форме - это более разумная вещь.
Ответ 2
Он работает так, как я думал:
PrintLength (с);
Вы передаете одну строку, которая является нулевой - внутри вашего метода, items
не будет null - это массив одного элемента - строки типа - значения null
PrintLength (s, s);
Одинаковая история здесь - вы передаете два элемента, поэтому items
в вашем методе будет представлять собой массив из двух строк - оба из которых имеют нуль, но массив не
PrintLength (нуль);
Это, очевидно, интерпретируется как одно значение NULL, и поэтому items
имеет значение null. Вы не передаете массив, ни элемент строки типа - вы просто передаете нулевое значение как таковое.
PrintLength (null, null);
Это снова - массив из двух элементов, оба из которых являются нулевыми, но массив сам по себе не является нулевым, поскольку вы передаете два значения.
Это немного озадачивает, может быть, но действительно: то, что вам нужно проверить в вашем методе PrintLength
, заключается не в том, является ли ваш items
в целом нулевым, но действительно ли фактические значения items[0]
и т.д. нуль.
Что является немного странным, может быть, или интуитивно понятным вначале - факт, что одно явное "нулевое" значение рассматривается как "null", а не массив одного элемента значения null. Почему это так, и может ли это быть реализовано по-другому - я не знаю, честно говоря.
Ответ 3
PrintLength(null)
передает нулевой массив, где as PrintLength(null, null)
передает string[]
с длиной двух, содержащей два объекта null string
. Это будет то же самое, что передать new string[] { null, null }
Хмм, читал, что я написал, может быть, это не отвечает на ваш вопрос.
Изменить:
Вероятно, поэтому: вы можете отправить список аргументов типа, указанного в объявлении параметра, или массив аргументов указанного типа.
http://msdn.microsoft.com/en-us/library/w5zay9db.aspx
Ответ 4
Как сказано в ответе:
-
1: массив строк с одним элементом в нем - элемент null
-
2: массив строк с двумя элементами в нем, оба элемента равны null
-
null: null передается в метод, а не массив
-
2: массив нулей передается в метод