Попробую уточнить ответ Энтони Пеграма.
Универсальный тип является ковариантным для некоторого аргумента типа, когда он возвращает значения указанного типа (например, Func<out TResult>
возвращает экземпляры TResult
, IEnumerable<out T>
возвращает экземпляры T
). То есть, если что-то возвращает экземпляры TDerived
, вы также можете работать с такими экземплярами, как если бы они были TBase
.
Общий тип является контравариантным по отношению к некоторому аргументу типа, когда он принимает значения указанного типа (например, Action<in TArgument>
принимает экземпляры TArgument
). То есть, если что-то нуждается в экземплярах TBase
, вы также можете передать в экземплярах TDerived
.
Кажется вполне логичным, что универсальные типы, которые принимают и возвращают экземпляры некоторого типа (если только он не определен дважды в сигнатуре универсального типа, например, CoolList<TIn, TOut>
), не являются ковариантными или контравариантными для соответствующего аргумента типа. Например, List
определяется в .NET 4 как List<T>
, а не List<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>
в качестве аргумента.