C# params object[] странное поведение

Учитывая этот код

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] strings = new string[] { "Test1", "Test2", "Test3" };

            int[] ints = new int[] { 1, 2, 3, 4 };

            Test(strings);

            Test(ints);
        }

        public static void Test(params object[] objects)
        {
        }
    }
}

И эта страница https://msdn.microsoft.com/fr-ca/library/w5zay9db.aspx

Я ожидал бы (params object[] objects) быть массивом одного элемента со строкой [] в качестве первого элемента, но когда я отлаживаю, я вижу, что (params object[] objects) это { "Test1", "Test2", "Test3" }.

Однако с int [] я получаю объект [] с int [] в качестве первого элемента.

Со строками

С целыми

Это неопределенное поведение? Это зависит от версии.Net Framework / Mono версии?

3 ответа

Решение

Хорошая находка!

Это неопределенное поведение?

Нет, это не совсем обычное поведение. Странный дизайн, но дизайн.

Это зависит от версии.Net Framework / Mono версии?

Нет. Все версии C# имеют такое поведение.

Это является следствием столкновения некоторых интересных правил C#.

Первое соответствующее правило: метод с массивом параметров может быть вызван в "нормальной" или "расширенной" форме. Нормальная форма такая, как будто не было "params". Расширенная форма принимает параметры и объединяет их в массив, который генерируется автоматически. Если обе формы применимы, то нормальная форма побеждает расширенную форму.

Теперь, возможно, это кажется разумным; если у вас есть массив объектов в руках, есть вероятность, что вы хотите передать массив объектов, а не массив, который содержит массив объектов.

Второе важное правило заключается в том, что C# допускает небезопасную ковариацию массива, когда тип элемента является ссылочным типом. То есть массив строк может быть неявно преобразован в массив объектов. Вы заметите, что это имеет два значения. Во-первых, это означает, что когда у вас есть массив объектов, это может быть массив строк, так что, скажем, вставка черепахи в этот массив объектов может вызвать ошибку типа. Это очень удивительно! Вы ожидаете, что каждый массив объектов может принимать любой объект, но это не так в C#. Некоторые массивы объектов лгут.

Второе следствие заключается в том, что, если поместить эту черепашку в массив строк, это означает, что каждый раз, когда вы помещаете что-либо в массив базового типа, среда выполнения должна проверять, что типы проверяются. Таким образом, записи в массиве обходятся дороже в C# при каждой записи, так что можно поймать ничтожно малое количество плохих записей.

Это беспорядок, и именно поэтому небезопасная ковариация массива возглавляет мой список неудачных возможностей C#.

Сочетание этих двух правил объясняет ваши наблюдения. Массив строк может быть преобразован в массив объектов, поэтому метод применим в обычной форме.

Для массива целых чисел ковариация не применяется к типам значений. Таким образом, массив целых чисел не может быть преобразован в массив объектов, поэтому этот метод неприменим в своей обычной форме. Но массив целых чисел является объектом, поэтому он применим в развернутом виде.

Смотрите также:

Почему params ведет себя так?

Ваш вопрос, возможно, является дубликатом:

Есть ли способ отвлечь myFunc(1, 2, 3) от myFunc(new int[] { 1, 2, 3 })?

Я не эксперт, но params Идея ключевого слова состояла в том, чтобы позволить возможность совершать различные вызовы метода независимо от того, сколько элементов у вас есть.

Test(object1)
Test(object1, object2)
Test(object1,..., objectN)

Итак, то, что вы видите, это нормальное поведение, ничего странного. Больше информации по этой ссылке msdn

Используя ключевое слово params, вы можете указать параметр метода, который принимает переменное число аргументов.

Вы можете отправить разделенный запятыми список аргументов типа, указанного в объявлении параметра, или массив аргументов указанного типа. Вы также можете отправить без аргументов. Если вы не отправляете аргументы, длина списка параметров равна нулю.

Никакие дополнительные параметры не разрешены после ключевого слова params в объявлении метода, и только одно ключевое слово params разрешено в объявлении метода.

public static void Test(params string[] strings)
{
}

Test(string1)
Test(string1, string2)

и так далее string1 ... stringN.

Другие вопросы по тегам