Hier ist eine seltsame Situation, die ich heute gesehen habe:
Ich habe eine generische Liste und möchte Artikel zu meiner Liste mit dem Indexer hinzufügen:
%Vor% Wenn ich das versuche, bekomme ich ArgumentOutOfRangeException
Dann habe ich List<T>
indexer set Methode betrachtet, und hier ist es:
Und auch auf Add
method:
Nun, wie ich sehe, verwenden beide Methoden den gleichen Weg:
%Vor% Das _items
ist ein Array vom Typ T
:
Und im Konstruktor _items
initialisiert wie folgt:
Nun, nach all diesen Dingen bin ich neugierig, warum ich keine Gegenstände mit einem Index
in meine Liste aufnehmen kann
, obwohl ich die Listenkapazität explizit festlege?
Der Grund ist, dass Sie nicht mit dem Indexer zur Liste hinzufügen, sondern vorhandene Elemente ersetzen .
Da Sie der Liste noch keine Elemente hinzugefügt haben, ist diese leer, und jeder Versuch, den Indexer zum Hinzufügen von Elementen zu verwenden, löst diese Ausnahme aus.
Dies:
%Vor%erstellt keine Liste mit 11 Elementen, sondern erstellt zunächst eine Liste mit Kapazität für 11 Elemente. Dies ist eine Optimierung. Wenn Sie weitere Elemente hinzufügen, muss die Größe der Liste intern angepasst werden, und Sie können die erwartete oder bekannte Kapazität übergeben, um zu viele dieser Größen zu vermeiden.
Hier ist ein LINQPad Programm, das Folgendes demonstriert:
%Vor%Ausgabe:
Interna
Intern wird innerhalb eines List<T>
ein Array verwendet, um die Elemente zu speichern. Darüber hinaus wird eine Count
Eigenschaft / Wert beibehalten, um zu verfolgen, wie viele dieser Array-Elemente tatsächlich verwendet wurden.
Wenn Sie eine leere Liste erstellen und keine Kapazität übergeben, wird eine Standardliste verwendet. Dies ist die anfängliche Größe dieses Arrays.
Wenn Sie der Liste immer wieder neue Elemente hinzufügen, füllen Sie langsam das Array gegen Ende auf. Sobald Sie das gesamte Array gefüllt und ein weiteres Element hinzugefügt haben, muss ein neues, größeres Array erstellt werden. Alle Elemente im alten Array werden dann in dieses neue, größere Array kopiert, und von nun an wird dieses Array verwendet.
Deshalb ruft der interne Code EnsureCapacity
method auf. Diese Methode führt bei Bedarf die Größenänderung aus.
Jedes Mal, wenn das Array in der Größe geändert werden muss, wird ein neues Array erstellt und alle Elemente werden kopiert. Wenn das Array wächst, wächst diese Operation in Kosten. Es ist nicht all so viel, aber es ist immer noch nicht frei.
Wenn Sie also wissen, dass Sie 1000 Elemente in der Liste speichern müssen, ist es eine gute Idee, zunächst einen Kapazitätswert zu übergeben. Auf diese Weise ist die anfängliche Größe dieses Arrays möglicherweise groß genug, um nie in der Größe geändert werden zu müssen. Gleichzeitig ist es keine gute Idee, einen sehr großen Wert für die Kapazität anzugeben, da dadurch viel Speicher unnötig wird.
Wissen Sie auch, dass alles in diesem Abschnitt der Antwort nicht dokumentiert ist (soweit ich weiß), und sollten Details oder spezifisches Verhalten, das Sie daraus lernen, den Code, den Sie schreiben, nicht beeinflussen, außer dem Wissen über das Bestehen in einem guten Kapazitätswert.
Nein, es ist nicht seltsam. Sie nehmen fälschlicherweise an, dass der Konstruktor, der die Kapazität akzeptiert, viele Elemente in der Liste initialisiert. Es tut nicht. Die Liste ist leer und Sie müssen Elemente hinzufügen.
Wenn Sie es aus irgendeinem Grund mit Elementen initialisieren müssen, können Sie dies tun:
%Vor% Klingt wie ein string[]
-Array, ist aber besser geeignet.
Es gibt einen Unterschied zwischen Kapazität der Liste und Anzahl der Liste.
Die Kapazität gibt an, wie groß das interne Array ist. Count gibt an, wie viele gültige Elemente die Liste enthält. Der Konstruktor, den Sie verwenden, legt die Kapazität fest, aber die Anzahl der gültigen Elemente ist immer noch 0. Und der Indexer prüft die Anzahl der gültigen Elemente, nicht die Kapazität.