Eu tenho uma private readonly
lista de LinkLabel
s ( IList<LinkLabel>
). Mais tarde, adiciono LinkLabel
s a esta lista e adiciono esses rótulos da FlowLayoutPanel
seguinte maneira:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
Mostra ReSharper me um aviso: Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
Por favor me ajude a descobrir:
- O que isso significa?
- Este é um controle de usuário e não será acessado por vários objetos para configurar rótulos, portanto, manter o código como tal não o afetará.
fonte
LinkLabel
(tipo especializado) paraControl
(tipo base).LinkLabel[]
paraControl[]
, que ainda é legal, mas pode ter um problema de tempo de execução. Tudo o que mudou foi a maneira como a matriz está sendo referenciada. A matriz em si não é alterada. Veja o problema? A matriz ainda é uma matriz do tipo derivado. A referência é através de uma matriz do tipo base. Portanto, é legal em tempo de compilação atribuir um elemento a ele do tipo base. No entanto, o tipo de tempo de execução não o suporta.Vou tentar esclarecer a resposta de Anthony Pegram.
O tipo genérico é covariante em algum argumento de tipo quando retorna valores desse tipo (por exemplo,
Func<out TResult>
retorna instâncias deTResult
,IEnumerable<out T>
retorna instâncias deT
). Ou seja, se algo retornar instâncias deTDerived
, você também poderá trabalhar com instâncias como se fossem deTBase
.O tipo genérico é contravariante em algum argumento de tipo quando aceita valores desse tipo (por exemplo,
Action<in TArgument>
aceita instâncias deTArgument
). Ou seja, se algo precisa de instâncias deTBase
, você também pode passar em instâncias deTDerived
.Parece bastante lógico que tipos genéricos que aceitam e retornam instâncias de algum tipo (a menos que sejam definidos duas vezes na assinatura de tipo genérica, por exemplo
CoolList<TIn, TOut>
) não sejam covariantes nem contravariantes no argumento de tipo correspondente. Por exemplo,List
é definido no .NET 4 comoList<T>
, nãoList<in T>
ouList<out T>
.Alguns motivos de compatibilidade podem ter causado a Microsoft a ignorar esse argumento e tornar as matrizes covariantes em seu argumento de tipo de valores. Talvez eles tenham realizado uma análise e tenham descoberto que a maioria das pessoas usa apenas matrizes como se fossem somente leitura (ou seja, elas usam apenas inicializadores de matriz para gravar alguns dados em uma matriz) e, como tal, as vantagens superam as desvantagens causadas por um possível tempo de execução erros quando alguém tenta usar covariância ao escrever na matriz. Por isso, é permitido, mas não incentivado.
Quanto à sua pergunta original,
list.ToArray()
cria uma novaLinkLabel[]
com os valores copiados da lista original e, para se livrar do aviso (razoável), você precisará passarControl[]
paraAddRange
.list.ToArray<Control>()
fará o trabalho:ToArray<TSource>
aceitaIEnumerable<TSource>
como argumento e retornaTSource[]
;List<LinkLabel>
implementa somente leituraIEnumerable<out LinkLabel>
, que, graças àIEnumerable
covariância, pode ser passado para o método que aceitaIEnumerable<Control>
como argumento.fonte
A "solução" mais direta
flPanel.Controls.AddRange(_list.AsEnumerable());
Agora, como você está mudando covariantemente
List<LinkLabel>
para,IEnumerable<Control>
não há mais preocupações, pois não é possível "adicionar" um item a um enumerável.fonte
O aviso é devido ao fato de que você poderia, teoricamente, adicionar um
Control
diferente deLinkLabel
aLinkLabel[]
àControl[]
referência a ele. Isso causaria uma exceção de tempo de execução.A conversão está acontecendo aqui porque
AddRange
leva aControl[]
.De maneira mais geral, converter um contêiner de um tipo derivado em um contêiner de um tipo base só é seguro se você não puder modificá-lo posteriormente da maneira descrita acima. Matrizes não atendem a esse requisito.
fonte
A causa raiz do problema está descrita corretamente em outras respostas, mas para resolver o aviso, você sempre pode escrever:
fonte
Com o VS 2008, não estou recebendo esse aviso. Isso deve ser novo no .NET 4.0.
Esclarecimento: de acordo com Sam Mackrill, é o Resharper que exibe um aviso.
O compilador C # não sabe que
AddRange
não modificará a matriz passada para ele. ComoAddRange
tem um parâmetro do tipoControl[]
, em teoria, poderia tentar atribuirTextBox
a ao array, o que seria perfeitamente correto para um array verdadeiro deControl
, mas o array é, na realidade, um array deLinkLabels
e não aceitará tal atribuição.Criar matrizes co-variantes no c # foi uma péssima decisão da Microsoft. Embora possa parecer uma boa ideia poder atribuir uma matriz de um tipo derivado a uma matriz de um tipo base, isso pode levar a erros de tempo de execução!
fonte
Que tal agora?
fonte
_list.ToArray<Control>()
.