J’ai une liste private readonly
de LinkLabel
( IList
). J’ajoute plus tard les LinkLabel
s à cette liste et ajoute ces étiquettes à un FlowLayoutPanel
comme suit:
foreach(var s in ssortingngs) { _list.Add(new LinkLabel{Text=s}); } flPanel.Controls.AddRange(_list.ToArray());
Resharper me Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
un avertissement: la Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
S’il vous plaît aidez-moi à comprendre:
Qu’est-ce que cela signifie est-ce
Control[] controls = new LinkLabel[10]; // comstack time legal controls[0] = new TextBox(); // comstack time legal, runtime exception
Et en termes plus généraux
ssortingng[] array = new ssortingng[10]; object[] objs = array; // legal at comstack time objs[0] = new Foo(); // again legal, with runtime exception
En C #, vous êtes autorisé à référencer un tableau d’objects (dans votre cas, LinkLabels) en tant que tableau d’un type de base (dans ce cas, en tant que tableau de contrôles). Il est également temps de comstackr pour atsortingbuer un autre object qui est un Control
au tableau. Le problème est que le tableau n’est pas un tableau de contrôles. A l’exécution, il s’agit toujours d’un tableau de LinkLabels. En tant que tel, l’affectation, ou écriture, lancera une exception.
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
renvoie des instances de TResult
, IEnumerable
renvoie des instances de T
). En d’autres TDerived
, si quelque chose renvoie des instances de TDerived
, vous pouvez aussi TDerived
des instances telles que celles de TBase
.
Le type générique est contravariant sur certains arguments de type lorsqu’il accepte des valeurs de ce type (par exemple, Action
accepte les instances de TArgument
). C’est-à-dire que si quelque chose a besoin d’instances de TBase
, vous pouvez également passer des instances de TDerived
.
Il semble tout à fait logique que les types génériques qui acceptent et retournent des instances d’un certain type (à moins que ce ne soit deux fois défini dans la signature de type générique, par exemple CoolList
) ne soient ni covariants ni contravariants dans l’argument de type correspondant. Par exemple, List
est défini dans .NET 4 comme List
, pas List
ou List
.
Certaines raisons de compatibilité ont pu amener Microsoft à ignorer cet argument et à rendre les tableaux covariants sur leur argument de type valeurs. Peut-être ont-ils procédé à une parsing et constaté que la plupart des utilisateurs n’utilisent les tableaux que s’ils étaient en lecture seule (c’est-à-dire qu’ils n’utilisent que des initialiseurs de tableaux pour écrire des données dans un tableau). des erreurs lorsque quelqu’un essaiera d’utiliser la covariance lors de l’écriture dans le tableau. Il est donc permis mais non encouragé.
En ce qui concerne votre question d’origine, list.ToArray()
crée un nouveau LinkLabel[]
avec les valeurs copiées de la liste d’origine et, pour se débarrasser des avertissements (raisonnables), vous devrez passer Control[]
à AddRange
. list.ToArray
fera le travail: ToArray
accepte IEnumerable
comme argument et retourne TSource[]
; List
implémente en lecture seule IEnumerable
, qui, grâce à la covariance IEnumerable
, pourrait être transmise à la méthode acceptant l’argument IEnumerable
.
L’avertissement est dû au fait que vous pouvez théoriquement append un Control
autre qu’un LinkLabel
au LinkLabel[]
via la référence Control[]
. Cela provoquerait une exception d’exécution.
La conversion se produit ici car AddRange
prend un Control[]
.
Plus généralement, la conversion d’un conteneur d’un type dérivé en conteneur d’un type de base n’est sûre que si vous ne pouvez pas modifier le conteneur par la suite. Les tableaux ne satisfont pas à cette exigence.
La “solution” la plus simple
flPanel.Controls.AddRange(_list.AsEnumerable());
Maintenant que vous modifiez de manière covariante List
en IEnumerable
il n’y a plus de soucis car il n’est pas possible “d’append” un élément à un énumérable.
La cause première du problème est correctement décrite dans d’autres réponses, mais pour résoudre cet avertissement, vous pouvez toujours écrire:
_list.ForEach(lnkLbl => flPanel.Controls.Add(lnkLbl));
Avec VS 2008, je ne reçois pas cet avertissement. Cela doit être nouveau pour .NET 4.0.
Précision: selon Sam Mackrill, c’est Resharper qui affiche un avertissement.
Le compilateur C # ne sait pas que AddRange
ne modifiera pas le tableau qui lui est passé. Comme AddRange
a un paramètre de type Control[]
, il pourrait en théorie essayer d’atsortingbuer un TextBox
au tableau, ce qui serait parfaitement correct pour un vrai tableau de Control
, mais le tableau est en réalité un tableau de LinkLabels
et n’acceptera pas une telle cession.
Rendre les tableaux co-variant dans c # était une mauvaise décision de Microsoft. Bien que cela puisse sembler une bonne idée de pouvoir atsortingbuer 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!
Que dis-tu de ça?
flPanel.Controls.AddRange(_list.OfType().ToArray());