J'ai une private readonly
liste de LinkLabel
s ( IList<LinkLabel>
). J'ajoute plus tard des LinkLabel
s à cette liste et ajoute ces étiquettes à un FlowLayoutPanel
comme suit:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
ReSharper me montre un avertissement: de Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
S'il vous plaît, aidez-moi à comprendre:
- Que cela veut-il dire?
- Il s'agit d'un contrôle utilisateur et ne sera pas accessible par plusieurs objets pour configurer les étiquettes, donc conserver le code en tant que tel ne l'affectera pas.
la source
LinkLabel
(type spécialisé) àControl
(type de base).LinkLabel[]
enControl[]
, qui est toujours légal, mais peut avoir un problème d'exécution. Tout ce qui a changé, c'est la façon dont le tableau est référencé. Le tableau lui-même n'est pas modifié. Vous voyez le problème? Le tableau est toujours un tableau du type dérivé. La référence se fait via un tableau du type de base. Par conséquent, il est légal au moment de la compilation de lui attribuer un élément du type de base. Pourtant, le type d'exécution ne le prendrait pas en charge.Je vais essayer de clarifier la réponse d'Anthony Pegram.
Le type générique est covariant sur un argument de type lorsqu'il renvoie des valeurs dudit type (par exemple,
Func<out TResult>
renvoie des instances deTResult
,IEnumerable<out T>
renvoie des instances deT
). Autrement dit, si quelque chose renvoie des instances deTDerived
, vous pouvez également travailler avec de telles instances comme si elles étaient deTBase
.Le type générique est contravariant sur certains arguments de type lorsqu'il accepte des valeurs dudit type (par exemple,
Action<in TArgument>
accepte des instances deTArgument
). Autrement dit, si quelque chose nécessite des instances deTBase
, vous pouvez également passer des instances deTDerived
.Il semble tout à fait logique que les types génériques qui acceptent et renvoient des instances d'un certain type (à moins qu'il ne soit défini deux fois dans la signature de type générique, par exemple
CoolList<TIn, TOut>
) ne soient ni covariants ni contravariants sur l'argument de type correspondant. Par exemple,List
est défini dans .NET 4 commeList<T>
, pasList<in T>
ouList<out T>
.Certaines raisons de compatibilité peuvent avoir amené Microsoft à ignorer cet argument et à rendre les tableaux covariants sur leur argument de type valeurs. Peut-être ont-ils mené une analyse et constaté que la plupart des gens n'utilisent que des tableaux comme s'ils étaient en lecture seule (c'est-à-dire qu'ils n'utilisent que des initialiseurs de tableau pour écrire certaines données dans un tableau), et, en tant que tels, les avantages l'emportent sur les inconvénients causés par une exécution possible. erreurs lorsque quelqu'un essaiera d'utiliser la covariance lors de l'écriture dans le tableau. Par conséquent, il est autorisé mais pas encouragé.
Quant à votre question d'origine,
list.ToArray()
crée une nouvelleLinkLabel[]
avec des valeurs copiées de la liste d'origine, et, pour se débarrasser de l'avertissement (raisonnable), vous devrez passerControl[]
àAddRange
.list.ToArray<Control>()
fera le travail:ToArray<TSource>
accepteIEnumerable<TSource>
comme argument et retourneTSource[]
;List<LinkLabel>
implémente en lecture seuleIEnumerable<out LinkLabel>
, qui, grâce à laIEnumerable
covariance, pourrait être passée à la méthode acceptantIEnumerable<Control>
comme argument.la source
La «solution» la plus simple
flPanel.Controls.AddRange(_list.AsEnumerable());
Maintenant que vous changez de manière covariante
List<LinkLabel>
en,IEnumerable<Control>
il n'y a plus de soucis puisqu'il n'est pas possible d '«ajouter» un élément à un énumérable.la source
L'avertissement est dû au fait que vous pourriez théoriquement ajouter un
Control
autre que aLinkLabel
auLinkLabel[]
par le biais de laControl[]
référence. Cela provoquerait une exception d'exécution.La conversion se produit ici car
AddRange
prend unControl[]
.Plus généralement, la conversion d'un conteneur d'un type dérivé en un conteneur d'un type de base n'est sûre que si vous ne pouvez pas modifier ultérieurement le conteneur de la manière décrite ci-dessus. Les tableaux ne satisfont pas à cette exigence.
la source
La cause première du problème est correctement décrite dans les autres réponses, mais pour résoudre l'avertissement, vous pouvez toujours écrire:
la source
Avec VS 2008, je ne reçois pas cet avertissement. Cela doit être nouveau dans .NET 4.0.
Précision: selon Sam Mackrill c'est Resharper qui affiche un avertissement.
Le compilateur C # ne sait pas que
AddRange
cela ne modifiera pas le tableau qui lui est passé. Puisqu'ilAddRange
a un paramètre de typeControl[]
, il pourrait en théorie essayer d'attribuer aTextBox
au tableau, ce qui serait parfaitement correct pour un vrai tableau deControl
, mais le tableau est en réalité un tableau deLinkLabels
et n'acceptera pas une telle affectation.La co-variante de tableaux en c # était une mauvaise décision de Microsoft. Bien que cela puisse sembler une bonne idée de pouvoir attribuer un tableau d'un type dérivé à un tableau d'un type de base en premier lieu, cela peut entraîner des erreurs d'exécution!
la source
Que dis-tu de ça?
la source
_list.ToArray<Control>()
.