Share via


Dr. GUI .NET #6

 

11. Juni 2002

Inhalte

Wo wir waren; Wohin wir gehen
Übersicht über Arrays und One-Dimensional Arrays
Arrays von Werttypen im Vergleich zu Arrays von Verweistypen
Array-Klasse
Mehrdimensionale Arrays
Konvertierungen von Arrays/Arrayelementen (Arraykovarianz)
Arrays von Arrays (oder "Ragged-Row"-Arrays)
Versuch es doch mal!
Was wir getan haben; Was kommt als nächstes

Sprechen Sie aus!

Wenn Sie etwas sagen möchten, teilen Sie uns (und der Welt) mit, was Sie über diesen Artikel auf dem Dr. GUI .NET-Meldungsboard halten.

Sehen Sie sich auch die Beispiele an, die als Microsoft® ASP.NET-Anwendungen mit Quellcode ausgeführt werden, oder werfen Sie einfach einen Blick auf die Quelldatei in einem neuen Fenster.

Wo wir waren; Wohin wir gehen

Das letzte Mal haben wir Zeichenfolgen im .NET Framework besprochen. Dieses Mal werden wir über Arrays im .NET Framework.

Übersicht über Arrays und One-Dimensional Arrays

Arrays in der .NET Framework sind ähnlich wie Arrays in jedem anderen Programmiersystem: Sie sind Datenstrukturen, die einen geordneten Satz von Werten enthalten. Alle Elemente müssen vom gleichen Typ sein. Beachten Sie jedoch, dass die Elemente bei Verweistypen auf ein Objekt des Elementtyps oder auf einen vom Elementtyp abgeleiteten Typ verweisen können. Das extreme Beispiel hierfür ist ein Array von Objects. Elemente in einem solchen Array können auf jeden Typ verweisen, da alle Typen von Object abgeleitet werden.

Sie greifen auf die Werte zu, indem Sie den Arraynamen und einen Index verwenden, bei dem es sich um einen ganzzahligen Ausdruck handelt. Der Index befindet sich in eckigen Klammern in C# und in Microsoft® Visual Basic® .NET in Klammern. Beispiel:

[C#]
double[] a = {0.1, 0.2, 0.3, 0.4, 0.5};
// gives "0.3": first subscript is zero, not one
StringBuilder sb = new StringBuilder(a[2].ToString());
int i = 2;
a[i] = a[i] + a[i - 1] / 4; // replaces 0.3 with 0.35
sb.AppendFormat(", now {0}\n", a[2].ToString());

[Visual Basic .NET]
Dim a() as Double = {0.1, 0.2, 0.3, 0.4, 0.5}
' gives "0.3": first subscript is zero, not one
Dim sb as New StringBuilder(a(2).ToString())
Dim i as Integer = 2
a(i) = a(i) + a(i - 1) / 4 ' replaces 0.3 with 0.35
sb.AppendFormat(", now {0}\n", a(2).ToString())

Im linken Teil der ersten Zeile im vorherigen Beispiel wird ein Arrayverweis namens "a" erstellt. Der Typ des Arrayverweis ist double []/Double() oder ein eindimensionales Array von double/Double. Beachten Sie, dass die Größe des Arrays nicht Teil seines Typs ist (dies unterscheidet sich von C und C++), obwohl der Rang oder die Anzahl der Dimensionen des Arrays Teil des Typs ist. Beachten Sie auch, dass Sie in C# schreiben Double ``[] a ``= { ... }; müssen, anstatt Double ``a[] ``= { ... }; wie in C oder C++ möglich. In Arraydeklarationen in C# werden die Arrayklammern mit dem Typ und nicht mit der Variablen verwendet. In Visual Basic .NET werden die Klammern mit der Variablen und nicht mit dem Typ verwendet. Verwirrend, oder? Dr. GUI denkt so....

Ein Arrayverweis kann auf ein Array einer beliebigen Größe verweisen, solange die Anzahl der Dimensionen (oder rang) und der Elementtyp identisch sind. (Der Arrayverweis kann natürlich auch NULL sein, anstatt auf ein tatsächliches Array zu verweisen.)

Der rechte Teil der ersten Zeile legt den Arrayverweis so fest, dass er auf ein Array zeigt, das über fünf Elemente verfügt. Die Tiefgestellten dieser Elemente sind null bis vier, nicht eins bis fünf. (In "klassischem" Visual Basic können Sie die Basis mit der "Option Base"-Anweisung auf 1 festlegen. Nicht mehr.) Das bedeutet, dass Sie eine Schleife schreiben würden, um alle Elemente wie folgt zu drucken:

[C#]
for (int i1 = 0; i1 <= 4; i1++)
   sb.AppendFormat("a[{1}] = {0}\n", a[i1], i1);

[Visual Basic .NET]
Dim i1 as Integer 
For i1 = 0 To 4
   sb.AppendFormat("a({1}) = {0}\n", a(i1), i1)
Next
sb.Replace("\n", Environment.NewLine)

Die vorangehende Schleife in C# legt für diejenigen von Ihnen, die (noch) nicht in C, C++ oder C# programmieren, i1 auf Null fest und führt dann den AppendFormat-Methodenaufruf fünfMal aus, wobei i1 nach jeder Ausführung erhöht wird. Es entspricht genau dem Für i1 = 0 bis 4 in der Visual Basic .NET-Version. Die Werte von i1 sind also 0, 1, 2, 3 und 4 (da der Test i1 <= 4 fehlschlägt, wenn i1 gleich fünf ist).

Beachten Sie, dass der gute Arzt die Möglichkeit nutzte, formatbezeichner im AppendFormat-Methodenaufruf umzuschalten, um die Parameter in der von ihm bevorzugten Reihenfolge zu platzieren.

Im Visual Basic .NET-Code konvertieren wir die Teilzeichenfolgen "\n" vor dem Beenden in CR/LF (und geben die Zeichenfolge vom StringBuilder zurück).

Off by One: Ein wichtiger Visual Basic .NET-Unterschied!

Ein sehr wichtiger Unterschied zwischen Visual Basic .NET und C# ist die Anzahl der Arrayelemente, die zugewiesen werden, wenn Sie eine feste Anzahl von Elementen anfordern.

Wenn Sie instance in C# sagenint [] a = new int[5];, erhalten Sie fünf Elemente, a[0] bis a[4].

Wenn Sie in Visual Basic .NET sagen Dim a() as Integer = New Integer(5), erhalten Sie SIX-Elemente, a[0] bis a[5].

Anders ausgedrückt: Visual Basic .NET fügt der Größe jeder Dimension automatisch und automatisch eine hinzu. Visual Basic tut dies aus älteren Gründen, um das Portieren alter Visual Basic-Programme zu Visual Basic .NET ein wenig zu vereinfachen.

Wenn wir also die oben genannten Arrays mit einer expliziten Größe zuordnen würden, würden wir 5 als Größe in C# und 4 als Größe in Visual Basic .NET verwenden, um genau dasselbe Array mit Elementen zu erhalten, die null bis vier nummeriert sind.

Diese Unterschiede führen zu Problemen. Besonders unangenehm ist es, wenn die Dimension eine Variable ist, insbesondere wenn diese Variable zwischen Routinen übergeben wird, die in verschiedenen Sprachen geschrieben wurden. Das Ergebnis ist, dass Ihre Arrays anders dimensioniert werden können als erwartet, was zu bösen Off-by-One-Fehlern und NULL-Zeiger-Verweisausnahmen führt.

Vorsicht! Vor allem, wenn Sie Sprachen mischen! Wenn Sie ein Visual Basic .NET-Programmierer sind, sollten Sie dies beachten, wenn Sie Arrays an .NET Framework Methoden übergeben.

Arrays sind Bounds-Checked

Wenn Sie das Array über- oder unterindiziert haben, generieren Sie eine IndexOutOfRangeException. Die .NET Framework erfordert, dass Arrayindizes überprüft werden, um sicherzustellen, dass sie sich in Grenzen befindet. Bei bestimmten Anwendungen, die besonders schwere Arraybearbeitungen durchführen, kann dies ein Leistungsproblem sein. Verwenden Sie für diese Anwendungen das Feature für unsicheren Code von C#, um Zeiger im C/C++-Stil und die Speicherverwaltung auszuführen. (Aber seien Sie vorsichtig!) Es gibt keine Entsprechung in Visual Basic .NET, daher ist es gut, dass Sie problemlos sprachenübergreifend im .NET Framework aufrufen können.

Arrays sind Verweistypen

Arrays im .NET Framework sind Verweistypen, sodass dieses Array entweder NULL/Nothing oder einen Verweis auf ein Array von double/Double enthalten kann. Seien Sie vorsichtig, wenn Sie einen Arrayverweis auf NULL/Nothing festlegen, da viele Programmierer nicht erwarten, dass ein Arrayverweis NULL/Nothing enthält. Sie können es stattdessen auf ein leeres Array (Array, das null Elemente enthält) festlegen:

[C#]
double[] a;   // a is unassigned (contains null)
a = null;   // you may not want to do this--see text above
a = new double[] { };      // an empty array is often better than null

[Visual Basic .NET]
Dim a() as Double   ' a is unassigned (contains Nothing)
a = Nothing   ' you may not want to do this--see text above
Erase a   ' same as above--simply sets a to Nothing
a = New Double() { }   ' an empty array is often better than Nothing

(Mit der Erase-Anweisung von Visual Basic wird der Arrayverweis auf Nothing festgelegt.)

Sie können auch Arrays zuweisen (und neu zuweisen), nachdem Sie die Variable deklariert haben. Für instance hätten wir den ersten Teil des vorherigen Beispiels wie folgt schreiben können:

[C#]
double[] a;   // a is unassigned
a = new double[] {0.1, 0.2, 0.3, 0.4, 0.5};   // can reassign
// also same as   double[] a = new double[]{0.1, 0.2, 0.3, 0.4, 0.5};

[Visual Basic .NET]
Dim a() as Double   ' a is unassigned
a = New Double(){0.1, 0.2, 0.3, 0.4, 0.5} ' can reassign
' also same as Dim a as double() new Double() {0.1, 0.2, 0.3, 0.4, 0.5}
ReDim Preserve a(8)   ' grows array to 8, preserves original values

Visual Basic .NET verfügt über eine ReDim-Anweisung , die in einer Form dasselbe tut, wie das einfache Neuzuweisen des Verweises. Es hat jedoch eine zweite Form, wobei die Schlüsselwort (keyword) Preserve verwendet wird, mit der Sie die Größe der letzten Dimension des Arrays nur ändern können, während sie den Inhalt des alten Arrays automatisch in das neue Array kopieren. (Wenn nicht wie oben genügend Elemente zum Kopieren vorhanden sind, werden die zusätzlichen Elemente mit 0 (null) initialisiert.)

Wie für alle Verweistypen gilt: Wenn Sie ein Array einem anderen zuweisen, verweisen beide auf dasselbe Array. Wenn Sie eine Kopie des Arrays möchten, müssen Sie es klonen, wie hier gezeigt:

[C#]
StringBuilder sb = new StringBuilder();
double[] b = a;   // b and a refer to same array
b[2] = 7.7;
sb.AppendFormat("a[2] is {0}, b[2] is {1}\n", a[2], b[2]); // both 7.7
double[] c = (double [])a.Clone();   // separate array
c[2] = 3.8;
sb.AppendFormat("a[2] is {0}, c[2] is {1}\n", a[2], c[2]); // NOT same

[Visual Basic .NET]
Dim sb as New StringBuilder
Dim b() as Double = a   ' b and a refer to same array
b(2) = 7.7
sb.AppendFormat("a(2) is {0}, b(2) is {1}\n", a(2), b(2)) ' both 7.7
Dim c() as Double = CType(a.Clone(), Double())   ' separate array
c(2) = 3.8
sb.AppendFormat("a(2) is {0}, c(2) is {1}\n", a(2), c(2)) ' NOT same
sb.Replace("\n", Environment.NewLine) ' change \n's

Arrays von Werttypen im Vergleich zu Arrays von Verweistypen

Bei Arrays aller Typen ist die Arrayvariable selbst ein Verweis auf ein Array. Aber was ist im Array enthalten?

Das Array, auf das sich der Arrayverweis bezieht, enthält zusätzlich zu den tatsächlichen Daten im Array einige interessante Informationen. Für instance enthält sie den Rang (Anzahl der Dimensionen) sowie die ober- und untere Grenze für jede Dimension des Arrays. (Obwohl die .NET Framework Arrays mit anderen unteren Grenzen als null zulässt, sind solche Arrays nicht CLS-kompatibel, sodass Sie sie in den meisten Sprachen nicht direkt deklarieren können und sie nicht für die meisten Ausnahmesituationen verwenden sollten.)

Aber c'mon! Wie sehen die Daten aus?

Im Folgenden wird beschrieben: Wenn es sich bei dem Array um ein Array eines Werttyps handelt, z. B. Int32 oder Double oder Char, oder um einen Ihrer eigenen Werttypen (deklariert mit dem Schlüsselwort (keyword) struct/Struct, für instance), enthält das Array tatsächlich die Daten.

Für instance sieht unser gerade angezeigtes Array in etwa wie folgt aus:

Abbildung 1. Array von Double, ein Werttyp

Alle Werttypen weisen ein ähnliches Layout auf. (Beachten Sie, dass a und b beide auf dasselbe Array verweisen, da wir dies so einrichten.)

Wenn wir jedoch ein Array von Verweistypen wie Zeichenfolgen hätten, würde das Array Verweise auf die Objekte (in diesem Fall Zeichenfolgen) und nicht auf die Zeichenfolgen selbst enthalten. Für instance können wir wie folgt ein Array von Zeichenfolgen einrichten:

[C#]
string[] sa = {"zero", "one", "two", "three", "four"};

[Visual Basic .NET]
Dim sa() as String = {"zero", "one", "two", "three", "four"}

Das Array würde im Arbeitsspeicher in etwa wie folgt aussehen:

Abbildung 2. Array von String, ein Verweistyp

Alle Arrays von Verweistypen weisen ein ähnliches Layout auf.

Beachten Sie, dass Verweistypen, genau wie bei einer skalaren Verweistypvariablen (nicht array) eine zusätzliche Dereferenzierungsebene bieten. Die Zeichenfolgenverweise werden im Array gespeichert. die Zeichenfolgen werden an anderer Stelle gespeichert. Der Compiler kümmert sich um die Dereferenzierung ordnungsgemäß, sodass Code wie der folgende einwandfrei funktioniert:

[C#]
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= 4; i++)
   sb.AppendFormat("sa[{1}] is {0}\n", sa[i], i);

[Visual Basic .NET]
Dim sb as New StringBuilder
Dim i as Integer
For i = 0 To 4
   sb.AppendFormat("sa({1}) is {0}\n", sa(i), i)
Next
sb.Replace("\n", Environment.NewLine)

Array-Klasse

Das eigentliche Arrayobjekt ist von einem speziellen Arraytyp. Alle Arraytypen werden von der Basisklasse System.Array abgeleitet. Und System.Array wird natürlich von System.Object abgeleitet. Dies bedeutet, dass Sie für jedes Array alles tun können, was Sie mit System.Array (und natürlich System.Object) tun können, da alle Arrays Objekte sind.

Wir haben die Dinge besprochen, die Sie mit einem Objekt in einer aktuellen Spalte tun können. Was wirklich interessant ist, ist, einige der Dinge zu betrachten, die Sie mit jedem Array tun können, indem Sie sich die System.Array-Klasse ansehen, von der alle Arrays abgeleitet werden.

Zunächst wird darauf hingewiesen, dass System.Array mehrere Schnittstellen implementiert: ICloneable, IList, ICollection und IEnumerable.

Das bedeutet, dass Sie jedes Array in eine dieser Schnittstellen umwandeln und jedes Array an eine Methode übergeben können, die ein Objekt von einer dieser Schnittstellen akzeptiert.

Klonen von Arrays

Die Tatsache, dass Arrays ICloneable implementieren, bedeutet, dass Sie ein Array klonen können, indem Sie Clone aufrufen. Wenn wir für instance nicht das array zuweisen, das nur durch Sagen b = aangezeigt wird, hätten wir es geklont, indem wir schreibenb = a.Clone(), dass wir eine Kopie des gesamten Arrays erstellt hätten, nicht nur des Verweises.

Die Clone-Methode kopiert jedoch nicht die Objekte, auf die das Array verweist. Wenn wir also oder Dim sb() as String = CType(sa.Clone(), string)schreibenstring[] sb = (string [])sa.Clone();, kopieren wir das Array der Zeichenfolgenverweise (die mittlere Spalte im vorherigen Diagramm), aber nicht die Zeichenfolgen selbst (rechts). Dies wird als "flache Kopie" bezeichnet. (Beachten Sie, dass die Umwandlung erforderlich ist, da Clone ein Objekt zurückgibt.) Dieses flache Kopierverhalten könnte nützlich sein, wenn zwei Arrays auf denselben Satz von Zeichenfolgen verweisen sollen – vielleicht würden wir ein Array von Zeichenfolgenverweisen rückwärts und das andere vorwärts sortieren.

Wenn wir eine "tiefe Kopie" durchführen möchten, könnten wir eine Schleife schreiben, um das Array zu erstellen und jede Zeichenfolge zu kopieren:

[C#]
public static string CloneStringArray() {
   string[] sa1 = {"rei", "ichi", "ni", "san", "shi"};
   // make sure sa1 is a one-dimensional array
   if ((sa1 is System.Array) && (sa1.Rank == 1)) {
 // create array of same size
      string[] sa2 = new string[sa1.Length];   
      for (int i = sa2.GetLowerBound(0); 
            i <= sa2.GetUpperBound(0); i++) {
         // can't use String.Clone--see below
         sa2[i] = String.Copy(sa1[i]);
      }
      sa2[0] = "zero";
      sa2[4] = "yon";
   // PrintRefArrayToString defined later...
      return PrintRefArrayToString(sa1, "sa1") + "\n" +
         PrintRefArrayToString(sa2, "sa2");
   } 
   else return "Could not clone array";
}

[Visual Basic .NET]
Function CloneStringArray() As String
    Dim sa1() As String = {"rei", "ichi", "ni", "san", "shi"}
    ' make sure sa1 is a one-dimensional array
    If IsArray(sa1) AndAlso sa1.Rank = 1 Then
        ' create array of same size
        Dim sa2(sa1.Length - 1) As String ' note off-by-one
        Dim i As Integer
        For i = sa2.GetLowerBound(0) To sa2.GetUpperBound(0)
            ' can't use String.Clone--see below
            sa2(i) = String.Copy(sa1(i))
        Next
        sa2(0) = "zero"
        sa2(4) = "yon"
        ' PrintRefArrayToString defined later...
        Return PrintRefArrayToString(sa1, "sa1") & Chr(10) & _
            PrintRefArrayToString(sa2, "sa2")
    Else
        Return "Could not clone array"
    End If
End Function

Sie werden feststellen, dass der gute Arzt hier drei zusätzliche Array-Methoden verwendet hat: Rank gibt die Anzahl der Dimensionen zurück; GetLowerBound gibt die untere Grenze der angegebenen Dimension zurück. und GetUpperBound gibt die Obergrenze der angegebenen Dimension zurück. In Visual Basic .NET gibt es auch die Funktionen LBound und RBound , die Sie für jedes Array verwenden können. In Visual Basic .NET können Sie mit IsArray erkennen, ob ein bestimmtes Objekt ein Array ist oder nicht. In C# haben wir die Syntax sa1 is System.Array verwendet, um dasselbe zu tun. Dies ist hier übermäßig (und tatsächlich gibt der Compiler eine Warnung aus), da wir bereits wissen, dass es sich um ein Array handelt, aber Dr. GUI wollte diese Dinge veranschaulichen.

Möglicherweise bemerken Sie auch den && Operator in C# und den AndAlso-Operator in Visual Basic .NET. Diese Operatoren fungieren als "Kurzschlussoperatoren". Wenn der Ausdruck links vom Operator false ist, wird die rechte Seite nicht einmal ausgewertet. In unserem Fall ist das gut, denn wenn sa1 kein Array ist, sollten wir nicht versuchen, dessen Rank-Eigenschaft zu lesen! Die entsprechenden Kurzschlussoperatoren "oder" befinden || sich übrigens in C# und OrElse (Dr. GUI liebt diesen Namen!) in Visual Basic .NET.

IEnumerable und foreach/For Each

System.Array implementiert auch IEnumerable, d. h. die Klasse kann standardmäßig aufgezählt werden. IEnumerable verfügt nur über eine Methode: GetEnumerator. Diese Methode erstellt und gibt ein separates Enumeratorobjekt zurück, das IEnumerator implementiert. (Dieses Objekt ist getrennt, sodass Sie mehr als einen Enumerator in derselben Auflistung haben können.)

Die IEnumerator-Schnittstelle verfügt über eine Eigenschaft mit dem Namen Current , die das aktuelle Objekt zurückgibt, auf das der Enumerator verweist, und zwei Methoden: MoveNext, die den Enumerator zum nächsten Element in der Auflistung verschiebt; und Zurücksetzen, wodurch der Enumerator auf kurz vor Beginn der Sammlung zurückgesetzt wird. (Beachten Sie, dass Sie MoveNext für einen neu erstellten Enumerator aufrufen müssen, um zum ersten Element zu gelangen.)

In C# und Visual Basic .NET gibt es eine Anweisung, die all diese Funktionen nutzt: foreach (in Visual Basic .NET heißt sie For Each ). Um ein gesamtes Array auszudrucken, können Sie instance wie folgt vorgehen:

[C#]
StringBuilder sb = new StringBuilder();
foreach (string s in sa)
   sb.AppendFormat("{0}***", s);

[Visual Basic .NET]
Dim sb as New StringBuilder
Dim s as String
For Each s In sa
   sb.AppendFormat("{0}***", s)
Next

Dies würde auch funktionieren, wenn sa ein mehrdimensionales Array ist! Beachten Sie jedoch, dass die meisten .NET-Compiler speziellen Code für foreach/For Each verwenden, wobei das Array ein eindimensionales Array mit einer Untergrenze von null ist. Anstatt über den Enumerator auf das Array zuzugreifen, generiert es speziell optimierten Code (der von der Runtime inlineiert werden kann), um auf das Array zuzugreifen. Sehen Sie sich den Code in ILDASM an, um den Unterschied zu sehen.

Beachten Sie, dass wir foreach/For Each im soeben gezeigten Deep-Copy-Beispiel nicht verwenden konnten, da Sie die Arrayelemente während der Aufzählung nicht ändern dürfen– foreach ist "schreibgeschützt". Stattdessen haben wir eine reguläre Schleife verwendet. Wenn Sie jedoch den Inhalt des Arrays nicht ändern und die Indizes nicht benötigen, sollten Sie foreach verwenden. Es ist einfacher, und der Compiler und die Runtime können sie besser optimieren.

Andere Schnittstellen

System.Array implementiert auch ICollection und IList. ICollection ist eine Schnittstelle (die übrigens von IEnumerable erbt), die die Eigenschaften Count, IsReadOnly, IsSynchronized und SyncRoot bereitstellt. Diese sind selbstverständlich, mit Ausnahme von SyncRoot, das ein Objekt zurückgibt, mit dem Sie synchronisieren können, um den Zugriff auf die Auflistung zu synchronisieren. Die -Schnittstelle stellt auch eine CopyTo-Methode zum Kopieren der Auflistung in ein Array bereit.

IList erbt von ICollection und fügt die Item-Eigenschaft hinzu, die dem C#-Indexer entspricht, sowie eine Reihe von Methoden für den Umgang mit Listen: Add, Insert, Remove, RemoveAt, Contains, IndexOf (gibt den Index eines Werts zurück) und Clear. Diese sind ziemlich offensichtlich. Weitere Informationen finden Sie in der Dokumentation zu System.Array . Außerdem werden die Methoden IsFixedSize und IsReadOnly hinzugefügt. Die Tatsache, dass Arrays IList implementieren, ermöglicht es Ihnen, Arrays und jede andere Datenstruktur, die IList implementiert, wie z. B. verknüpfte Listen, gleich zu behandeln. Beachten Sie, dass die Member der IList-Schnittstelle mit expliziten Implementierungen implementiert werden, was bedeutet, dass Sie den Arrayverweis in den IList-Typ umwandeln müssen, um diese Methoden aufrufen zu können. Sie können sie nicht direkt in einem Array aufrufen. Beachten Sie weiter, dass Insert, Remove und RemoveAt nicht funktionieren– sie lösen NotSupportedException aus. Dies liegt daran, dass Arrays eine feste Größe haben, sodass es nicht möglich ist, Elemente einzufügen oder zu entfernen.

Andere Elemente von System.Array

Die Liste der Mitglieder von System.Array ist lang, und die meisten Namen der Funktionen beschreiben, was sie gut können. Der gute Arzt listet also nur die Namen der Mitglieder auf, die keine Schnittstellen implementieren, und erklärt diejenigen, die nicht offensichtlich sind.

Zunächst gibt es eine Reihe statischer Methoden: BinarySearch, Clear, Copy (kopiert Einen Teil eines Arrays in ein anderes), CreateInstance (ermöglicht es Ihnen, alle Arten von Arrays programmgesteuert zu erstellen, einschließlich Arrays, deren Untergrenze(n) nicht null ist/sind), IndexOf und LastIndexOf, Reverse und Sort.

Wir haben bereits alle Eigenschaften und die meisten der instance Methoden erläutert, aber es gibt noch einige weitere: GetLength, CopyTo, Initialize, GetValue und SetValue.

Wir haben die am häufigsten verwendeten ausführlich erläutert, daher können Sie sich den Rest in der Dokumentation zu Array-Mitgliedern ansehen.

In der Regel verwenden Sie Array.Initialize nur, wenn Sie eine eigene .NET Framework Sprache entwickeln.

Mehrdimensionale Arrays

Die .NET Framework unterstützt auch echte mehrdimensionale Arrays. (Einige andere Sprachen – Dr. GUI benennt keine Namen, aber der Name der Sprache beginnt mit "J" – unterstützen überhaupt keine mehrdimensionalen Arrays, sondern nur weniger effiziente Arrays von Arrays. Die .NET-Runtime unterstützt beides. Die Wahl ist gut.)

Auch hier ist die Größe jeder Dimension nicht Teil des Typs des Arrays, sodass ein zweidimensionales Array von ints den Typ "int [ , ]" und ein dreidimensionales Array von Zeichenfolgens den Typ "string [ , , ]" aufweisen würde.

Die Deklarations- und Initialisierungssyntax für mehrdimensionale Arrays ist der syntax für eindimensionale Arrays sehr ähnlich. Der Hauptunterschied besteht darin, dass in Initialisierern jede Dimension in geschweifte Klammern eingeschlossen werden muss:

[C#]
int [,,] x = { { {1, 2, 3, 4},   {5, 6, 7, 8},     {9, 10, 11, 12} },
 { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} }
};

[Visual Basic .NET]
Dim x(,,) as Integer = _
     { { {1, 2, 3, 4},     {5, 6, 7, 8},       {9, 10, 11, 12} }, _
 { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } }

Im vorherigen Beispiel ist x ein 2 x 3 x 4-Array von int/Integer. Jede Zeile/Spalte/usw. eines mehrdimensionalen Arrays muss die gleiche Größe aufweisen wie alle anderen In derselben Dimension.

Die foreach/For Each-Anweisung durchläuft alle Elemente eines mehrdimensionalen Arrays, wobei zuerst die äußerste rechte Dimension zunimmt:

[C#]
StringBuilder sb = new StringBuilder();
foreach (int i in x)
   sb.AppendFormat("{0} ", i); // 1 2 3 4 5 6 ... 24

[Visual Basic .NET]
Dim sb as New StringBuilder()
Dim i as Integer
For Each i in x
   sb.AppendFormat("{0} ", i) ' 1 2 3 4 5 6 ... 24
Next

Die gängigere Methode für den Umgang mit mehrdimensionalen Arrays ist jedoch eine Reihe von geschachtelten Schleifen:

[C#]
sb = new StringBuilder();
int iMax = x.GetUpperBound(0);
int jMax = x.GetUpperBound(1);
int kMax = x.GetUpperBound(2);
for (int i = 0; i <= iMax; i++)
   for (int j = 0; j <= jMax; j++)
      for (int k = 0; k <= kMax; k++)
         sb.AppendFormat("{0} ", x[i, j, k]); // 1 2 3 4 ... 24

[Visual Basic .NET]
sb = new StringBuilder()
Dim j, k As Integer
Dim iMax as Integer = x.GetUpperBound(0)
Dim jMax as Integer = x.GetUpperBound(1)
Dim kMax as Integer = x.GetUpperBound(2)
For i = 0 To iMax
   For j = 0 To jMax
      For k = 0 to kMax
         sb.AppendFormat("{0} ", x(i, j, k)) ' 1 2 3 4 ... 24
Next k
   Next j
Next i

Beachten Sie, dass GetLowerBound nicht aufgerufen wurde. Sie können es fast immer durch 0 ersetzen, da die untere Grenze der meisten Arrays – und aller CLS-konformen Arrays – null ist. Wir haben auch temporäre Variablen verwendet, anstatt GetUpperBound mehrmals innerhalb geschachtelter Schleifen aufzurufen. (Das Deklarieren und Verwenden von Konstanten für die Obergrenzen wäre noch besser.)

Konvertierungen von Arrays/Arrayelementen (Arraykovarianz)

Für Arrays von Verweistypen können Sie von einem Arraytyp in einen anderen konvertieren, vorausgesetzt, dass die beiden Arraytypen denselben Rang aufweisen und eine implizite oder explizite Konvertierung von einem Typ in einen anderen erfolgt. Für instance können Sie ein Array von Zeichenfolgenüberall dort verwenden, wo Sie ein Array von Object-Objektenverwenden können, da eine implizite Konvertierung von einer Zeichenfolge in ein Objekt erfolgt.

Aufgrund der Kovarianz müssen Arrayelementzuweisungen typgeprüft werden.

Zum Beispiel:

[C#]
public static string PrintRefArrayToString(object [] oa, string name) {
   StringBuilder sb = new StringBuilder();
   for(int i = 0; i < oa.Length; i++)
      sb.AppendFormat("{0}[{2}] is {1}\n", name, oa[i], i);
   return sb.ToString();
}
void FillArray(object [] oa, object o) {
   for (int i = 0; i < oa.Length; i++)
      // assignment *IS* type-checked!
      oa[i] = o;
}

// ...
StringBuilder sb = new StringBuilder();
string [] s = {"One", "Two", "Three"};
sb.Append(PrintRefArrayToString(s, "s"));
FillArray(s, "Hello");
sb.Append(PrintRefArrayToString(s, "s"));
FillArray(s, null);
sb.Append(PrintRefArrayToString(s, "s"));
// FillArray(s, 0); // FAILS with exception in assignment
// int [] ia = {0, 1, 2};
// FillArray(ia, 0); // FAILS--no covariance for value types

[Visual Basic .NET]
Function PrintRefArrayToString(ByVal oa() As Object, _
    Byval name As String) As String
    Dim sb As New StringBuilder()
    Dim i As Integer
    For i = 0 To oa.Length - 1
        sb.AppendFormat("{0}({2}) is {1}\n", name, oa(i), i)
    Next
    sb.Replace("\n", Environment.NewLine)
    Return sb.ToString()
End Function

Sub FillArray(oa() as Object, o as Object)
   Dim i as Integer
   For i = 0 To oa.Length – 1 ' off-by-one
      ' assignment *IS* type-checked!
      oa(i) = o
   Next
End Sub

' ...
Dim sb As New StringBuilder()
Dim s() As String = {"One", "Two", "Three"}
sb.Append(PrintRefArrayToString(s, "s"))
FillArray(s, "Hello")
sb.Append(PrintRefArrayToString(s, "s"))
FillArray(s, Nothing)
sb.Append(PrintRefArrayToString(s, "s"))
' FillArray(s, 0) ' FAILS with exception in assignment
' Dim ia() As Integer = {0, 1, 2}
' FillArray(ia, 0) ' FAILS--no covariance for value types

Wie bereits erwähnt, gilt die Arraykovarianz jedoch nur für Verweistypen. Sie können ein Array von Werttypen (z. B. ein Array von int/Integer) nicht in einen anderen Arraytyp konvertieren– nicht einmal in ein Array von Object, da Sie eine Kopie eines Arrays von Werttypen erstellen müssten, um es zu konvertieren. Sie können natürlich jedes Array in System.Array oder Object konvertieren.

Arrays von Arrays (oder "Ragged-Row"-Arrays)

Die .NET Framework unterstützt Arrays von Arrays. (Obwohl Visual Basic .NET nicht für Beta 1 geeignet war, ist dies jetzt der Fall – und das ist eine gute Sache.)

Arrays von Arrays können für Situationen nützlich sein, in denen Sie für instance etwas benötigen, das etwa wie ein zweidimensionales Array aussieht, aber die Zeilen unterschiedlich lang sind. Da jedes Zeilenarray separat zugeordnet wird, können Sie jede Zeile auf eine beliebige Länge festlegen– sie muss nicht alle dieselbe Länge haben.

Der Zugriff kann etwas langsamer sein als auf ein echtes mehrdimensionales Array (da Sie mehr als einmal Dereferenzierung und Begrenzungsüberprüfung durchführen müssen), und das Erstellen eines solchen Arrays erfordert mehr Speicherbelegungen und Zeit, aber wenn die Zeilengrößen sehr unterschiedlich sind und die Datenstruktur groß ist, können Sie eine erhebliche Menge an Arbeitsspeicher sparen.

Das Erstellen und Initialisieren von Arrays von Arrays umfasst immer eine Schleife für die Initialisierung, da es nicht möglich ist, die Unterarrays in derselben Anweisung zu erstellen, die das Standard Arrays erstellt. Wenn Sie für instance ein Array von 20 Arrays mit int/Integer mit unterschiedlicher Länge verwenden möchten, können Sie Folgendes schreiben:

[C#]
int [][] arrOfArr = new int [5][];   // can't specify a row size here
for (int i = 0; i <= 4; i++) {
   int rowLen = i + 3;   // different row size for each row
   arrOfArr[i] = new int[rowLen];   // create sub-array
   for (int j = 0; j < rowLen; j++)
// initialize elements of sub-array
      arrOfArr[i][j] = i * 100 + j;   
}
StringBuilder sb = new StringBuilder();
foreach (int[] ia in arrOfArr) { // have to use nested foreach
   foreach (int i in ia)
      sb.AppendFormat("{0} ", i);
   sb.Append("\n");
}

[Visual Basic .NET]
' Note: Visual Basic automatically gives one more element, so we 
' allocate one less
Dim arrOfArr(4)() as Integer ' can't specify a row size
Dim i as Integer
For i = 0 to 4
   Dim rowLen as Integer = i + 3   ' different row sizes for each row
   arrOfArr(i) = New Integer(rowLen - 1) {} ' create sub-array
   Dim j as Integer
   For j = 0 To RowLen - 1
      ' initialize elements of sub-array
      arrOfArr(i)(j) = i * 100 + j
   Next j
Next i
Dim sb as New StringBuilder
Dim ia() as Integer
For Each ia in arrOfArr ' have to use nested For Each
   ' reuse i
   For Each i in ia
      sb.AppendFormat("{0} ", i)
   Next
   sb.Append(Environment.NewLine)
Next

Beachten Sie, dass Sie kein foreach/For Each-Objekt über das Array von Arrays ausführen können, aber Sie können geschachtelte foreach/For Each-Instanzenausführen.

Versuch es doch mal!

Wer ist in Ihrem .NET Framework Learning Team?

Der gute Arzt hofft, dass Sie nicht nur mit .NET spielen, sondern auch mit einigen anderen Leuten zusammenarbeiten. Auf diese Weise macht es mehr Spaß, und Sie werden garantiert mehr erfahren.

Einige Dinge, die Sie ausprobieren können...

Spielen Sie mit Arrays und Zeichenfolgen – und mit Arrays von Zeichenfolgen. Stellen Sie sicher, dass Sie mindestens ein- und zweidimensionale Arrays ausprobieren. Vielleicht machen Sie ein Spiel, das auf Arrays basiert, z. B. Conways Spiel des Lebens (was wir das nächste Mal tun werden).

Das Board ist eine zweidimensionale Matrix, in der ein Anfangsmuster als Seeding verwendet wird. Jeder Punkt in der Matrix lebt, stirbt oder gebiert ein neues Leben gemäß den folgenden Regeln. Hier ist eine Beschreibung:

Das Spiel des Lebens wurde von John Conway erfunden (wie Sie vielleicht gesammelt haben). Das Spiel wird auf einem Feld von Zellen gespielt, von denen jede acht Nachbarn (angrenzende Zellen) hat. Eine Zelle ist entweder besetzt (von einem Organismus) oder nicht. Die Regeln für das Ableiten einer Generation aus der vorherigen sind die folgenden:

  • Tod: Wenn eine besetzte Zelle 0, 1, 4, 5, 6, 7 oder 8 besetzte Nachbarn hat, stirbt der Organismus (0, 1 Nachbarn: an Einsamkeit; 4 bis 8: der Überfüllung).
  • Überleben: Wenn eine besetzte Zelle zwei oder drei Nachbarn hat, überlebt der Organismus bis zur nächsten Generation.
  • Geburt: Wenn eine unbesetzte Zelle drei besetzte Nachbarn hat, wird sie besetzt.

Versuchen Sie, die Datenbindung in einer ASP.NET-Anwendung oder einer Microsoft® Windows® Forms-Anwendung zu verwenden– sehen Sie sich die Funktionsweise an.

Was wir getan haben; Was kommt als nächstes

Dieses Mal haben wir über Arrays gesprochen. Das nächste Mal zeigen wir Arrays mit John Conways Game of Life als ASP.NET Anwendung.