Почему передача null в метод params приводит к массиву нулевых параметров?
У меня есть метод, который использует ключевое слово params
, например:
private void ParamsMethod(params string[] args)
{
// Etc...
}
Затем я вызываю метод, используя различные комбинации аргументов:
// Within the method, args is...
ParamsMethod(); // - a string array with no elements
ParamsMethod(null); // - null (Why is this?)
ParamsMethod((string)null); // - a string array with one element: null
ParamsMethod(null, null); // - a string array with two elements: null and null
ParamsMethod("s1"); // - a string array with one element: "s1"
ParamsMethod("s1", "s2"); // - a string array with two elements: "s1" and "s2"
Я понимаю все случаи, кроме второго. Может ли кто-нибудь объяснить, почему ParamsMethod(null)
вызывает args
как null
, а не массив с одним нулевым элементом?
Ответы
Ответ 1
A params
параметр предназначен только для того, чтобы обеспечить удобный способ указания значений - вы все равно можете напрямую передать ссылку на массив.
Теперь null
можно конвертировать в string[]
или string
, поэтому обе интерпретации действительны - это до спецификации, которая является предпочтительной. Спецификация заявляет в разделе 10.6.1.4, что:
-
Аргумент, заданный для массива параметров, может быть единственным выражением, которое неявно конвертируется в тип массива параметров. В этом случае массив параметров действует точно как параметр значения.
-
В качестве альтернативы, [...]
Другими словами, компилятор проверяет, является ли аргумент действительным как "обычный" тип параметра первым, и только строит массив, если это абсолютно необходимо.
Ответ 2
См. спецификацию С#, раздел 10.6.1.4 Массивы параметров:
Массив параметров позволяет указать аргументы одним из двух способов при вызове метода:
- Аргумент, заданный для массива параметров, может быть единственным выражением, которое неявно конвертируется (§6.1) в тип массива параметров. В этом случае массив параметров действует точно так же, как параметр значения.
- В качестве альтернативы, вызов может указывать ноль или более аргументов для массива параметров, где каждый аргумент является выражением, которое неявно конвертируется (§6.1) в тип элемента массива параметров. В этом случае вызов создает экземпляр типа массива параметров с длиной, соответствующей количеству аргументов, инициализирует элементы экземпляра массива с заданными значениями аргументов и использует только что созданный экземпляр массива в качестве фактического аргумента.
Так как null
неявно конвертируется в string[]
, он будет использоваться как массив.
Далее
При выполнении разрешения перегрузки метод с массивом параметров может быть применим либо в его нормальной форме, либо в расширенной форме (§7.5.3.1). Расширенная форма метода доступна только в том случае, если нормальная форма метода не применима и только если метод с той же сигнатурой, что и расширенная форма, еще не объявлен в том же типе.
который поясняет, что компилятор действительно сначала пытается использовать метод с аргументом массива (нормальная форма), прежде чем он попытается использовать аргумент как элемент массива (расширенная форма).
Ответ 3
Причиной этого является то, что вы можете передать массив соответствующего типа аргументу params
. Поскольку null
может быть преобразован в любой тип, а синтаксис массива имеет приоритет, вы получаете null
как значение вашего параметра.
Явное выделение его в string
, конечно, делает его элементом массива, а не самим массивом.
Вы можете попробовать и посмотреть:
private void DoSomething(params IEnumerable[] arr) {
// ...
}
...
DoSomething(new IEnumerable[] {new int[] {}}); // arr[0] isn't IEnumerable[], it int[].
И здесь онлайн-демонстрация.
Ответ 4
Вы можете передать массив параметру params - на самом деле, это предпочтительнее (Т.е. если вы передадите объект [] методу, который принимает объект params [], это параметр цели целых, а не только один элемент). Null является действительным как назначение массиву, поэтому - эта привязка выигрывает.
В принципе, вы не будете более явными.
Ответ 5
Из спецификации языка:
Массив параметров позволяет указать аргументы одним из двух способов при вызове метода:
-
Аргумент, заданный для массива параметров, может быть единственным выражением, которое неявно конвертируется (§6.1) в тип массива параметров. В этом случае массив параметров действует точно как параметр значения.
-
В качестве альтернативы, вызов может указывать ноль или более аргументов для массива параметров, где каждый аргумент является выражением, которое неявно конвертируется (§6.1) в тип элемента массива параметров. В этом случае вызов создает экземпляр типа массива параметров с длиной, соответствующей количеству аргументов, инициализирует элементы экземпляра массива с заданными значениями аргументов и использует только что созданный экземпляр массива в качестве фактического аргумента.
Так как null
в ParamsMethod(null)
может быть неявно преобразован в (string[])null
, первое правило будет применяться.