Tipp-Upload: VB.NET 0036: Arrays, Listen, Dictionaries - Initialisierung + Mehrdimensionalität
von Spatzenkanonier
Über den Tipp
Dieser Vorschlag soll VB.NET Tipp 2 ersetzen.
Dieser Tippvorschlag ist noch unbewertet.
Der Vorschlag ist in den folgenden Kategorien zu finden:
- Sprachmerkmale
Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
Array, List, Dictionary, initialisierung
Der Vorschlag wurde erstellt am: 05.09.2007 10:08.
Die letzte Aktualisierung erfolgte am 06.02.2009 20:57.
Beschreibung
Hier werden drei grundlegende Klassen des Frameworks 2.0 eingehender betrachtet - es wird auf Möglichkeiten und Fallstricke hingewiesen
Als obsolete Auflistungstypen bitte nicht weiter verwenden:
System.Collections.ArrayList(Framework 1.1)
Microsoft.VisualBasic.Collection(VB6).
(Letztere nicht zu verwechseln mit der generischen System.Collections.ObjectModel.Collection(Of T) )
Da hier vor allem syntaktische Eigenarten beleuchtet werden, empfiehlt sich beim Testlauf, im Prozedurschritt (F10) vorzugehen, und die Ausgabe im AusgabeFenster zu beachten.
Schwierigkeitsgrad |
Verwendete API-Aufrufe: |
Download: |
' Dieser Source stammt von http://www.activevb.de ' und kann frei verwendet werden. Für eventuelle Schäden ' wird nicht gehaftet. ' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. ' Ansonsten viel Spaß und Erfolg mit diesem Source! ' ' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird. ' In den Zip-Dateien ist er jedoch zu finden. ' --------- Anfang Projektgruppe ArraysUndListen.sln --------- ' -------- Anfang Projektdatei ArraysUndListen.vbproj -------- ' ----------------- Anfang Datei modMain.vb ----------------- ' Projekteinstellungen: ' Option Explicit On ' Option Strict On ' Imports Microsoft.VisualBasic.ControlChars ' Imports System Imports System.Windows.Forms Public Module modMain Public Sub Main() ' Array-Deklaration - beachte, daß der oberste gültige Index (UpperBound) um eines ' geringer ist als die Anzahl (Length) der Elemente. ' Dieses, weil auch die 0 ein gültiger Index ist Const Length0 As Integer = 10 Dim Arr0(Length0 - 1) As Integer ' zwei Varianten, die Elemente zu enumerieren For I As Integer = 0 To Length0 - 1 Arr0(I) = I Next For Each Value As Integer In Arr0 WriteLine(Value) Next WriteLine() ' Bei der initialisierenden Deklaration entfällt die Angabe der Anzahl der Elemente - das ' "sieht" der Compiler selbst Dim Words As String() = New String() {"If", "Then", "Else"} ' Man kann auch knapper formulieren Dim SmartWords As String() = {"If", "Then", "Else"} ' noch einmal der Unterschied zwischen UpperBound und Länge eines Arrays: WriteLine("UpperBound: ", Words.GetUpperBound(0), ", Length: ", Words.Length, Lf) ' Arrays haben die schnellsten Zugriffszeiten, den geringsten Speicherverbrauch, und die ' Elemente sind typisiert. ' Außerdem bietet die Array-Klasse einen umfangreichen Satz schneller und nützlicher ' Methoden, (auf die ich hier nicht eingehe): IndexOf(), Find(), BinarySearch(), Sort(), ' Reverse(), CopyTo(). ' ## Es können aber keine Elemente hinzugefügt oder entfernt werden. ## ' Redimensionieren erstellt ein neues Array, und kopiert ggfs. (Preserve) die Werte um. ' Das ist manchmal im Einzelfall sinnvoll (Optimierung auf Zugriffszeiten), aber v.a. ' Schleifen wie diese sind außerordentlich unperformant: For I As Integer = 0 To Length0 - 1 ReDim Preserve Arr0(I) Arr0(I) = I Next ' stattdessen kann man die List(Of T) verwenden: Dim List0 As New List(Of Integer) For I As Integer = 0 To Length0 - 1 List0.Add(I) Next ' Auf List(Of T) kann man dieselben beiden For-Schleifen anwenden wie auf T-Array ' List(Of T) kann man auch initialisieren - etwa mit einem Array Dim List1 As New List(Of String)(New String() {"If", "Then", "Else"}) ' Leider kann man List(Of T) nicht mit einem ParamArray initialisieren ' Da schafft ein kleines Helfer-Funktiönchen etwas Abhilfe Dim List2 As List(Of String) = NewList("If", "Then", "Else") ' Kommen wir zur Mehrdimensionalität Call MultiDimensions() End Sub Public Function NewList(Of T)(ByVal ParamArray Items() As T) As List(Of T) Return New List(Of T)(Items) End Function Sub MultiDimensions() Dim Arr0(3, 1) As Integer ' Bei "echter Mehrdimensionalität" im Array reicht die Array.Length-Property nicht mehr ' aus, man muß den UpperBound der einzelnen Dimensionen ermitteln Dim YUbound As Integer = Arr0.GetUpperBound(0) Dim XUbound As Integer = Arr0.GetUpperBound(1) For Y As Integer = 0 To YUbound For X As Integer = 0 To XUbound ' (zur Erinnerung: UpperBound ist eins kleiner als die Länge des Arrays) Arr0(Y, X) = Y * (XUbound + 1) + X Next Next ' Kuriose ForEach-Iteration: Arr0 wird wie ein eindimensionales Array ausgelesen For Each Numb As Integer In Arr0 WriteLine(Numb) Next ' Merkspruch: "Zeile zuerst, Spalte später" ' Es geht darum, daß es mathematisch gesehen egal ist, ob ein 2-dimensionales Array seine ' Elemente zunächst von oben nach unten durchgeht, und sich dann die nächste Spalte ' vornimmt, oder ob es von links nach rechts liest, und dann die nächste Zeile. ' Zu vermeiden ist nur, sein Array erst spaltenweise aufzubauen, und später dann ' zeilenweise auszulesen (außer, man beabsichtigt dieses explizit). ' Daher o.g. Konvention, der auch der Bildschirm-Aufbau folgt, oder etwa die alltägliche ' Lese-Richtung in der westlichen Kultur. ' Und bei derlei Arrays bedeuten die Indizees eben: "Zeile zuerst, Spalte später" ' Beweis: Initialisiere String(2, 1) ( = 3 Zeilen, 2 Spalten ) Dim ArrDic As String(,) = {{"If", "Wenn"}, {"Then", "Dann"}, {"Else", "Andernfalls"}} ' Der Merkspruch gilt eben auch für den Aufbau verschachtelter Schleifen. For Y As Integer = 0 To 2 ' Zeile zuerst For X As Integer = 0 To 1 ' Spalte später Write(ArrDic(Y, X)) Next WriteLine() Next WriteLine() ' Fallstricke lauern beim Umgang mit Grafik und mit Tabellen-Controls, da diese gegen o.g. ' Merkspruch verstoßen: Dim Pt As New System.Drawing.Point(X:=3, Y:=2) Dim DGV As New DataGridView : DGV.RowCount = 1 : DGV.ColumnCount = 1 Dim C As DataGridViewCell = DGV(columnIndex:=0, rowIndex:=0) ' zur Vertiefung die Initialisierung eines 3-dimensionalen Arrays - beachten Sie den ' Einsatz der geschweiften Klammern Dim Wuerfel(,,) As Integer = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}} ' ( Upperbound = 1, in allen 3 Dimensionen ) For Z As Integer = 0 To 1 For Y As Integer = 0 To 1 For X As Integer = 0 To 1 Write(Wuerfel(Z, Y, X)) Next WriteLine() Next WriteLine() Next WriteLine() ' "unechte Mehrdimensionalität" - Array von Arrays ' Angenommen, Sie brauchen ein Array, daß in Zeile Eins drei Einträge enthält, in Zeile ' zwei hingegen vier? ' Verwenden Sie ein Array von Arrays: Dim KeyWords As String()() = New String()() { New String() {"If", "Else", "End " & _ "If"}, New String() {"Try", "Catch", "Finally", "End Try"}, New String() { _ "Do", "Loop"}, New String() {"For", "Next"}, New String() {"Imports"}} For Y As Integer = 0 To KeyWords.Length - 1 For X As Integer = 0 To KeyWords(Y).Length - 1 ' Beim Zugriff hat jeder Index seine eigene Klammer, im Gegensatz zur "echten" ' Mehrdimensionalität Write(KeyWords(Y)(X)) Next WriteLine() Next WriteLine() ' ForEach mal wieder eleganter: For Each Line As String() In KeyWords For Each Word As String In Line Write(Word) Next WriteLine() Next WriteLine() ' "unechte" Mehrdimensionalität mit List(Of T): Dim KeyWordList As List(Of List(Of String)) = NewList(NewList("If", "Else", "End " & _ "If"), NewList("Try", "Catch", "Finally", "End Try"), NewList("Do", "Loop"), _ NewList( "For", "Next"), NewList("Imports")) ' spaßeshalber mal ohne NewList()-Funktion (alle Listen mit Arrays initialisieren): KeyWordList = New List(Of List(Of String))(New List(Of String)() { New List(Of _ String)(New String() {"If", "Else", "End If"}), New List(Of String)(New String() _ {"Try", "Catch", "Finally", "End Try"}), New List(Of String)(New String() { _ "Do", "Loop"}), New List(Of String)(New String() {"For", "Next"}), New List(Of _ String)(New String() {"Imports"})}) ' So eine Initialisierung ist nun wirklich nicht mehr sehr leserlich ' Dafür kann man aber sowohl Worte als auch ganze Zeilen nachträglich einfügen oder ' entfernen: KeyWordList(0).Insert(1, "Then") ' einer Gruppe ein Element zufügen KeyWordList.Insert(2, NewList("While", "Wend")) ' eine Gruppe zufügen KeyWordList.RemoveAt(KeyWordList.Count - 1) ' letzte Gruppe entfernen ' Der Zugriff ist identisch mit dem Zugriff bei Array-Arrays For Y As Integer = 0 To KeyWordList.Count - 1 For X As Integer = 0 To KeyWordList(Y).Count - 1 Write(KeyWordList(Y)(X)) Next WriteLine() Next WriteLine() Call Dictionaries() End Sub Sub Dictionaries() ' Während Arrays und Listen einen numerisch indizierten Zugriff gewähren, bietet das ' Dictionary einen Zugriff über Schlüssel-**Objekte** ' Den Datentyp von Schlüssel und Wert kann man per generischem Parameter festlegen; hier ' ein einfaches Beispiel für Strings: Dim Dic As New Dictionary(Of String, String) With Dic .Add("If", "Wenn") .Add("Then", "Dann") .Add("Else", "Andernfalls") End With WriteLine("Dic(""If"") = ", Dic("If")) ' Eine manchmal(!) praktische Kuriosität beim Schreibzugriff: Existiert der Schlüssel noch ' gar nicht, so wird er immanent angelegt. Dic("End If") = "Ende Wenn" ' Beim Lese-Zugriff auf einen nicht vorhandenen Schlüssel erfolgt natürlich eine ' KeyNotFoundException Dim Response As String = Dic("Try") ' Dictionaries sind ebenfalls enorm schnell im Zugriff. ' Intern werden die Werte nach dem sog. Hashing-Verfahren "eingeräumt", was einen ' direkten Abruf ohne Such-Algorithmus ermöglicht ' Enumerieren kann man ein Dictionary auch, sogar in mehrfacher Hinsicht: For Each Entry As KeyValuePair(Of String, String) In Dic WriteLine(Entry.Key, " = ", Entry.Value) Next ' oder For Each Key As String In Dic.Keys WriteLine(Key) Next ' oder For Each Value As String In Dic.Values WriteLine(Value) Next ' anders als bei Listen haben wir hier aber **keinen numerischen Index** ' Dim P As KeyValuePair(Of String, String) = Dic(2) '<- geht nicht ' Beachten Sie, daß die .Keys() und .Values()-Property jeweils ein **Export** des ' Dictionaries ist. ' D.h., bei jedem Abruf wird intern eine Collection erstellt und befüllt, also das gesamte ' Dictionary durchlaufen. ' folgender Code ist also 3 mal performanter (an Geschwindigkeit *und* Speicherplatz) als ' der darauf folgende: Dim Values As Dictionary(Of String, String).ValueCollection = Dic.Values For I As Integer = 0 To 2 For Each Val As String In Values ' machwas Next Next ' unperformant For I As Integer = 0 To 2 For Each Val As String In Dic.Values ' machwas Next Next End Sub End Module ''' <summary>zwei kleine Helferlein zur vereinfachten Ausgabe</summary> Public Module modHelpers Public Sub WriteLine(ByVal ParamArray Args() As Object) Console.WriteLine(String.Concat(Args)) End Sub Public Sub Write(ByVal Arg As Object) Console.Write(String.Concat(Arg, " ")) End Sub End Module ' ------------------ Ende Datei modMain.vb ------------------ ' --------- Ende Projektdatei ArraysUndListen.vbproj --------- ' ---------- Ende Projektgruppe ArraysUndListen.sln ----------
Diskussion
Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.
Folgende Diskussionen existieren bereits
Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.