Почему Resharper говорит, что преобразование ковариантного массива из строки [] в объект [] может вызвать исключение во время выполнения при операции записи с этим кодом?

Этот код:

comboBoxMonth.Items.AddRange(UsageRptConstsAndUtils.months.ToArray());

public static List<String> months = new List<String>
{
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec"
};

Превращает R # в скрягу с жалобой: «Преобразование ковариантного массива из строки [] в объект [] может вызвать исключение во время выполнения при операции записи».

На самом деле этот код работает отлично - поле со списком заполняется значениями месяца; о чем Resharper и что я могу сделать, чтобы развеять его сомнения?

Если дело просто в том, что общий список может содержать неверные данные, я не буду об этом беспокоиться — и если когда-нибудь возникнет проблема, ее будет достаточно легко отследить.


person B. Clay Shannon    schedule 05.11.2015    source источник
comment
Я предполагаю, что способ обойти это - использовать List<object> вместо List<string>.   -  person Ron Beyer    schedule 06.11.2015


Ответы (2)


Метод comboBoxMonth.Items.AddRange ожидает параметр object[]. months.ToArray() это string[]. Приведение от string[] к object[] допустимо, но если метод попытается изменить элементы массива, вы получите ошибки во время выполнения. В данном случае это не так, поэтому вы можете игнорировать предупреждение.

Если вас это раздражает, вы можете использовать ToArray<object>()

comboBoxMonth.Items.AddRange(UsageRptConstsAndUtils.months.ToArray<object>());

Он вернет object[], и приведение не потребуется.

person Jakub Lortz    schedule 05.11.2015
comment
Что вы подразумеваете под если метод пытается изменить элементы массива? Изменение элемента уже в массиве не должно быть проблемой. - person Yuval Itzchakov; 06.11.2015
comment
@YuvalItzchakov: Если метод попытается присвоить банан одному элементу массива объектов, который на самом деле является массивом строк, то во время выполнения произойдет сбой. Люди разумно ожидают, что присвоить банан массиву объектов удастся, но это может и не получиться. - person Eric Lippert; 06.11.2015
comment
@EricLippert Да, я понимаю, и именно это я пытался передать в своем ответе. Но я думаю, что в предложении есть двусмысленность, в которой говорится, что изменение существующих элементов вызовет исключение во время выполнения. - person Yuval Itzchakov; 06.11.2015
comment
@YuvalItzchakov: Видимо, я не понимаю ход ваших мыслей. Как это двусмысленно? - person Eric Lippert; 06.11.2015
comment
@YuvalItzchakov Ошибки выполнения возникнут, если функция, получающая этот массив, будет сохранять его как Object[] внутри (хотя на самом деле это String[]), а затем попытается заменить один из элементов в нем чем-то, что не строка. Пример: Object[] arr = new String[5]; arr[2] = 3; — вполне корректный код, но при его запуске произойдет сбой. - person Nyerguds; 23.05.2016
comment
@EricLippert Я думаю, он предположил, что функция преобразует полученный аргумент в реальный массив типа Object. - person Nyerguds; 23.05.2016
comment
@Nyerguds Я даже не помню, о чем я думал: X. Перечитав это сейчас, мне становится все ясно. - person Yuval Itzchakov; 23.05.2016

Пример для демонстрации проблемы:

void Main()
{
    Animal[] animals = new Girafee[2];
    animals[0] = new Zebra();
}

public class Animal { }
public class Girafee : Animal { }
public class Zebra : Animal { }

Это вызовет ArrayTypeMismatchException во время выполнения.

R# в основном намекает вам на возможную проблему, связанную с тем, что вы присваиваете string[] object[], что полностью разрешено компилятором, но может привести к исключению во время выполнения, если объект, который имеет одну и ту же базу class, присваивается массиву, который уже указывает на другой тип (как в моем примере, мы фактически указываем на массив жирафа). Ковариация массива нарушена в том смысле, что она не обеспечивает безопасность во время компиляции, которую вы получаете с дженериками.

Эрик Липперт говорит об этом в Ковариация и контравариантность в C#, часть вторая: ковариация массивов:

К сожалению, этот особый вид ковариации нарушается. Он был добавлен в CLR, потому что этого требует Java, а разработчики CLR хотели иметь возможность поддерживать Java-подобные языки. Затем мы добавили его в C#, потому что он был в CLR. Это решение было довольно спорным в то время, и я не очень этому рад, но сейчас мы ничего не можем с этим поделать.

Почему это сломано? Потому что всегда должно быть законно помещать Черепаху в массив Животных. С ковариантностью массива в языке и среде выполнения вы не можете гарантировать, что массив Animals может принять Turtle, потому что резервное хранилище может фактически быть массивом Giraffes.

person Yuval Itzchakov    schedule 05.11.2015