У меня есть private readonly
список LinkLabel
s ( IList<LinkLabel>
). Позже я добавляю LinkLabel
s в этот список и добавляю эти метки к FlowLayoutPanel
следующему:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
ReSharper показывает мне предупреждение: Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
Пожалуйста, помогите мне разобраться:
- Что это значит?
- Это пользовательский элемент управления, и к нему не будут обращаться несколько объектов для настройки меток, поэтому сохранение кода как такового не повлияет на него.
источник
LinkLabel
(специализированный тип) кControl
(базовый тип).LinkLabel[]
вControl[]
, которое все еще разрешено, но может иметь проблемы во время выполнения. Все, что изменилось, - это способ ссылки на массив. Сам массив не меняется. Видите проблему? Массив по-прежнему является массивом производного типа. Ссылка осуществляется через массив базового типа. Следовательно, во время компиляции можно назначить элемент базового типа. Тем не менее, тип среды выполнения не поддерживает его.Я постараюсь уточнить ответ Энтони Пеграма.
Универсальный тип является ковариантным для некоторого аргумента типа, когда он возвращает значения указанного типа (например,
Func<out TResult>
возвращает экземплярыTResult
,IEnumerable<out T>
возвращает экземплярыT
). То есть, если что-то возвращает экземплярыTDerived
, вы также можете работать с такими экземплярами, как если бы они былиTBase
.Универсальный тип является контравариантным для некоторого аргумента типа, когда он принимает значения указанного типа (например,
Action<in TArgument>
принимает экземплярыTArgument
). То есть, если чему-то нужны экземплярыTBase
, вы также можете передать экземплярыTDerived
.Кажется вполне логичным, что универсальные типы, которые одновременно принимают и возвращают экземпляры некоторого типа (если он не определен дважды в сигнатуре универсального типа, например
CoolList<TIn, TOut>
), не являются ковариантными и контравариантными для соответствующего аргумента типа. Например,List
в .NET 4 определено какList<T>
notList<in T>
илиList<out T>
.Некоторые причины совместимости могли заставить Microsoft проигнорировать этот аргумент и сделать массивы ковариантными по аргументу типа значений. Возможно, они провели анализ и обнаружили, что большинство людей используют массивы только так, как если бы они были только для чтения (то есть они используют инициализаторы массива только для записи некоторых данных в массив), и, как таковые, преимущества перевешивают недостатки, вызванные возможной средой выполнения. ошибки, когда кто-то попытается использовать ковариацию при записи в массив. Следовательно, это разрешено, но не поощряется.
Что касается вашего исходного вопроса,
list.ToArray()
создается новыйLinkLabel[]
со значениями, скопированными из исходного списка, и, чтобы избавиться от (разумного) предупреждения, вам нужно перейтиControl[]
кAddRange
.list.ToArray<Control>()
выполнит свою работу:ToArray<TSource>
принимает вIEnumerable<TSource>
качестве аргумента и возвращаетTSource[]
;List<LinkLabel>
реализует доступ только для чтенияIEnumerable<out LinkLabel>
, который, благодаряIEnumerable
ковариантности, может быть передан методу, принимающему вIEnumerable<Control>
качестве аргумента.источник
Наиболее прямолинейное «решение»
flPanel.Controls.AddRange(_list.AsEnumerable());
Теперь, когда вы ковариантно меняете
List<LinkLabel>
на,IEnumerable<Control>
больше нет проблем, так как невозможно «добавить» элемент в перечислимое.источник
Предупреждение связано с тем, что теоретически вы могли бы добавить к объекту ссылку, отличную
Control
от a . Это вызовет исключение во время выполнения.LinkLabel
LinkLabel[]
Control[]
Преобразование происходит здесь, потому что
AddRange
принимает файлControl[]
.В более общем плане преобразование контейнера производного типа в контейнер базового типа безопасно только в том случае, если вы не можете впоследствии изменить контейнер только что описанным способом. Массивы не удовлетворяют этому требованию.
источник
Основная причина проблемы правильно описана в других ответах, но для устранения предупреждения вы всегда можете написать:
источник
В VS 2008 я не получаю этого предупреждения. Это должно быть в новинку .NET 4.0.
Уточнение: по словам Сэма Макрилла, это Resharper отображает предупреждение.
Компилятор C # не знает, что
AddRange
не будет изменять переданный ему массив. ПосколькуAddRange
имеет параметр типаControl[]
, он теоретически может попытаться присвоитьTextBox
массиву a , что было бы совершенно правильно для истинного массиваControl
, но на самом деле массив является массивомLinkLabels
и не примет такое назначение.Создание ковариантных массивов в C # было плохим решением Microsoft. Хотя может показаться хорошей идеей в первую очередь иметь возможность назначать массив производного типа массиву базового типа, это может привести к ошибкам во время выполнения!
источник
Как насчет этого?
источник
_list.ToArray<Control>()
.