Ich habe eine private readonly
Liste von LinkLabel
s ( IList<LinkLabel>
). Ich füge später LinkLabel
s zu dieser Liste hinzu und füge diese Bezeichnungen FlowLayoutPanel
wie folgt hinzu:
foreach(var s in strings)
{
_list.Add(new LinkLabel{Text=s});
}
flPanel.Controls.AddRange(_list.ToArray());
Resharper zeigt mir eine Warnung : Co-variant array conversion from LinkLabel[] to Control[] can cause run-time exception on write operation
.
Bitte helfen Sie mir herauszufinden:
- Was bedeutet das?
- Dies ist ein Benutzersteuerelement, auf das nicht mehrere Objekte zugreifen, um Beschriftungen einzurichten. Wenn Sie also den Code als solchen beibehalten, hat dies keine Auswirkungen darauf.
quelle
LinkLabel
(Spezialtyp) zuControl
(Basistyp).LinkLabel[]
nachControl[]
, was zwar legal ist, aber ein Laufzeitproblem haben kann. Alles, was sich geändert hat, ist die Art und Weise, wie auf das Array verwiesen wird. Das Array selbst wird nicht geändert. Sehen Sie das Problem? Das Array ist immer noch ein Array vom abgeleiteten Typ. Die Referenz erfolgt über ein Array vom Basistyp. Daher ist es zur Kompilierungszeit zulässig, ihm ein Element des Basistyps zuzuweisen. Der Laufzeittyp würde dies jedoch nicht unterstützen.Ich werde versuchen, die Antwort von Anthony Pegram zu klären.
Generischer Typ ist covariant auf irgendeine Art Argument , wenn es Werte des Typs zurückgibt (zB
Func<out TResult>
kehrt InstanzenTResult
,IEnumerable<out T>
kehrt InstanzenT
). Das heißt, wenn etwas Instanzen von zurückgibtTDerived
, können Sie auch mit solchen Instanzen arbeiten, als ob sie von wärenTBase
.Der generische Typ ist bei einigen Typargumenten kontravariant, wenn er Werte dieses Typs
Action<in TArgument>
akzeptiert (z. B. Instanzen vonTArgument
). Das heißt, wenn etwas Instanzen von benötigtTBase
, können Sie auch Instanzen von übergebenTDerived
.Es erscheint ziemlich logisch, dass generische Typen, die Instanzen eines bestimmten Typs akzeptieren und zurückgeben (es sei denn, sie sind beispielsweise zweimal in der generischen
CoolList<TIn, TOut>
Typensignatur definiert), für das entsprechende Typargument weder kovariant noch kontravariant sind. BeispielsweiseList
ist in .NET 4 definiert alsList<T>
, nichtList<in T>
oderList<out T>
.Einige Kompatibilitätsgründe haben möglicherweise dazu geführt, dass Microsoft dieses Argument ignoriert und Arrays für ihr Wertetypargument kovariant gemacht hat. Vielleicht haben sie eine Analyse durchgeführt und festgestellt, dass die meisten Leute Arrays nur so verwenden, als wären sie schreibgeschützt (dh sie verwenden nur Array-Initialisierer, um einige Daten in ein Array zu schreiben), und als solche überwiegen die Vorteile die Nachteile, die durch die mögliche Laufzeit verursacht werden Fehler, wenn jemand versucht, beim Schreiben in das Array die Kovarianz zu nutzen. Daher ist es erlaubt, aber nicht ermutigt.
Wie für Ihre ursprüngliche Frage,
list.ToArray()
eine neue erstelltLinkLabel[]
mit den Werten aus ursprünglichen Liste kopiert, und, um loszuwerden, (angemessene) Warnung, müssen Sie in übergebenControl[]
zuAddRange
.list.ToArray<Control>()
wird den Job machen:ToArray<TSource>
akzeptiertIEnumerable<TSource>
als Argument und kehrt zurückTSource[]
;List<LinkLabel>
implementiert schreibgeschütztIEnumerable<out LinkLabel>
, was dank derIEnumerable
Kovarianz an die Methode übergeben werden kann, dieIEnumerable<Control>
als Argument akzeptiert .quelle
Die einfachste "Lösung"
flPanel.Controls.AddRange(_list.AsEnumerable());
Jetzt, da Sie kovariant zu wechseln
List<LinkLabel>
,IEnumerable<Control>
gibt es keine Bedenken mehr, da es nicht möglich ist, ein Element zu einer Aufzählung hinzuzufügen.quelle
Die Warnung ist auf die Tatsache zurückzuführen, dass Sie theoretisch ein
Control
anderes als einLinkLabel
zu demLinkLabel[]
durch denControl[]
Verweis darauf hinzufügen könnten . Dies würde eine Laufzeitausnahme verursachen.Die Konvertierung findet hier statt, weil
AddRange
aControl[]
.Im Allgemeinen ist das Konvertieren eines Containers eines abgeleiteten Typs in einen Container eines Basistyps nur dann sicher, wenn Sie den Container anschließend nicht wie oben beschrieben ändern können. Arrays erfüllen diese Anforderung nicht.
quelle
Die Hauptursache des Problems wird in anderen Antworten korrekt beschrieben. Um die Warnung zu beheben, können Sie jedoch immer schreiben:
quelle
Mit VS 2008 erhalte ich diese Warnung nicht. Dies muss neu in .NET 4.0 sein.
Klarstellung: Laut Sam Mackrill ist es Resharper, der eine Warnung anzeigt.
Der C # -Compiler weiß nicht, dass
AddRange
das an ihn übergebene Array nicht geändert wird. DaAddRange
es einen Parameter vom Typ hatControl[]
, könnte es theoretisch versuchen,TextBox
dem Array ein zuzuweisen , was für ein echtes Array von vollkommen korrekt wäreControl
, aber das Array ist in Wirklichkeit ein Array vonLinkLabels
und akzeptiert eine solche Zuordnung nicht.Es war eine schlechte Entscheidung von Microsoft, Arrays in c # als Co-Variante zu erstellen. Es scheint zwar eine gute Idee zu sein, einem Array eines Basistyps ein Array eines abgeleiteten Typs zuzuweisen, dies kann jedoch zu Laufzeitfehlern führen!
quelle
Wie wäre es damit?
quelle
_list.ToArray<Control>()
.