Rubrik: Variablen/Strings · Array/ArrayList | VB-Versionen: VB2005, VB2008 | 17.06.08 |
Klasse für nicht-nullbasierte Arrays Manchmal sind nicht-nullbasierte Arrays nützlich. Diese Klasse bietet dafür einen Vorschlag. | ||
Autor: Manfred Bohn | Bewertung: | Views: 10.429 |
ohne Homepage | System: Win2k, WinXP, Win7, Win8, Win10, Win11 | Beispielprojekt auf CD |
In VB.Net sind die System.Arrays stets null-basiert. Falls man den Array-Index nur als eine Zugriffshilfe auf die einzelnen Array-Elemente^betrachtet, ist das keine besondere Einschränkung.
Unter bestimmten Umständen kann es aber nützlich sein, wenn dem Array-Index auch eine inhaltliche Bedeutung zuzuordnen ist.
Wertet man z.B. jahresbezogene Zeitreihen aus, die von 'ErstesJahr' (z.B. 1970) bis 'LetztesJahr' (z.B. 2008) erhoben worden sind, ist es zweckmäßig, die Daten in einem Array aufzubewahren, dessen Dimension von 1970 bis 2008 läuft.
Der Array-Index besitzt in diesem Fall zugleich die Bedeutung: Jahreszahl.
Die generische Klasse cArrayXToY erlaubt die Erstellung von ein- und mehrdimensionalen Arrays, deren Dimensions-Untergrenze(n) nicht 0 sein müssen, sondern beliebig wählbar sind. Für die Obergrenze jeder Dimension gilt, dass sie >= Untergrenze sein muß.
Die Erstellung von Instanzen der Klasse erfolgt durch den Konstruktor (New), der zugleich den Datentyp des Array, die Zahl der Dimensionen und die Unter- und Obergrenzen jeder Dimension festlegt. (Vgl. Kommentarzeilen und Anwendungsbeispiel). Zusätzlich werden Hilfswerte für den effizienten Zugriff erstellt.
Die Grenzen einer Array-Dimension können durch die Funktionen lbd/ubd abgefragt werden (1. Dimension = 0!).
(Bitte beachten Sie, dass die Klasse nur für die integrierten numerischen Datentypen und den Datentyp String getestet worden ist. Bei der Verwendung von Klassen oder Strukturen als Array-Elemente sind ggf. zusätzliche Gesichtspunkte zu berücksichtigen.)
Die Klasse löst sowohl bei der Array-Deklaration (Konstruktor) als auch beim Array-Zugriff Ausnahmen aus, falls Dimensionen oder Indizes nicht plausibel sind.
Auf die Implementierung der Methoden für Redim(Preserve), Clone und CopyTo ist der Einfachheit halber verzichtet worden. Die Anwendung solcher Funktionen auf nicht-nullbasierte Arrays ist ohnehin meist weniger empfehlenswert. Übersichtlicher ist es, das Zielarray (ggf. mit den Grenzen) explizit zu erstellen und die gewünschten Elemente in Schleifen sukzessive aus dem Quellarray zu übertragen.
Hinweis für VB6-Umsteiger:
Wenn Sie sich jetzt wundern, liegen Sie richtig. Diese Einschränkung gab es in VB6-Arrays nicht. Auch in den aktuellen VB-Versionen ist es zwar möglich, ein Array durch Dim arr(x to y) zu deklarieren. Aber: x muss dabei stets 0 sein.
Nicht-nullbasierte Arrays stehen nur noch im Rahmen der COM.Interop-Kompatibilität zur Verfügung.
Während der Umstell-Phase von VB6-Programmen kann dies für Testzwecke nützlich sein. (vgl. Nicht-nullbasierte Arrays in VB.NET)
Vorsicht ist bei der Verwendung des Upgrade-Assistenten geboten.
Die VB6-Dimensionierung:
Dim x(90 to 100, 90 to 100, 90 to 100)
wird wie folgt umgestellt:
' UPGRADE_WARNING: ' Lower bound of array x was changed from 90,90,90 to 0,0,0. Dim x(100, 100, 100) As Object
Die Klasse cArrayXToY
''' <summary> ''' generische Klasse zur Verwendung eines gekapselten 1D-Array ''' als nicht-nullbasiertes (ggf. auch mehrdimensionales) Array ''' </summary> ''' <typeparam name="EntryType">numerischer Typ oder String</typeparam> Public Class cArrayXToY(Of EntryType) ' Struktur für Daten einer Arraydimension Structure Limit Dim ug As Integer ' Untergrenze Dim og As Integer ' Obergrenze Dim elemente As Integer ' Länge End Structure Dim gLimits() As Limit ' Dimensionsgrenzen des Array Dim gDimFak() As Integer ' Hilfs-Faktoren zur Indexberechnung ' das gekapselte System.Array ' (-->null-basiert, ein-dimensional) Dim gArray() As EntryType
''' <summary> ''' Konstruktor (incl. Vereinbarung der Dimensionsgerenzen) ''' </summary> ''' <param name="Limits">Dimensionsgrenzen: ug1, og1, ug2, og2, ...</param> Public Sub New(ByVal ParamArray Limits() As Integer) ' Array auf 'Nichts' setzen gArray = Nothing ' Limits überprüfen If UBound(Limits) > 20 Or UBound(Limits) < 1 Then Throw New ArgumentException End If If UBound(Limits) Mod 2 <> 1 Then Throw New ArgumentException End If ' Überprüfung der Limits fortsetzen und speichern ReDim gLimits(Limits.Length \ 2 - 1) Dim j As Integer = -1, l As Integer = 1 For i As Integer = 0 To UBound(Limits) - 1 Step 2 j += 1 gLimits(j).ug = Limits(i) gLimits(j).og = Limits(i + 1) If gLimits(j).ug > gLimits(j).og Then Throw New ArgumentException End If gLimits(j).elemente = _ gLimits(j).og - gLimits(j).ug + 1 l *= gLimits(j).elemente Next i ' gekapseltes 1D-Array in der angeforderten Größe dimensionieren ReDim gArray(l - 1) ' Zu überspringende Elementzahl pro Indexwert einer Dimension ' => Multiplikation der Elementzahl aller höheren Dimensionen ReDim gDimFak(UBound(gLimits)) For i As Integer = 0 To UBound(gLimits) gDimFak(i) = 1 For k As Integer = i + 1 To UBound(gLimits) gDimFak(i) *= gLimits(k).elemente Next k Next i End Sub
''' <summary> ''' Integrierte LBOUND-Funktion: Untergrenze der Dimension ''' </summary> ''' <param name="Dimension">Dimension?</param> ''' <returns>Untergrenze</returns> Public Function Lbd(Optional ByVal Dimension As Integer = 0) As Integer If IsNothing(gArray) Then Throw New NullReferenceException End If If Dimension > _ Microsoft.VisualBasic.UBound(gLimits) Then Throw New RankException End If Return gLimits(Dimension).ug End Function
''' <summary> ''' Integrierte UBOUND-Funktion: Obergrenze der Dimension ''' </summary> ''' <param name="Dimension">Dimension?</param> ''' <returns>Obergrenze</returns> Public Function Ubd( _ Optional ByVal Dimension As Integer = 0) As Integer If IsNothing(gArray) Then Throw New NullReferenceException End If If Dimension > Microsoft.VisualBasic.UBound(gLimits) Then Throw New RankException End If Return gLimits(Dimension).og End Function
''' <summary> ''' Standardeigenschaft: Zuweisung oder Abfrage ''' eines Arrayelements ''' </summary> ''' <param name="ind1">Index 1. Dimension</param> ''' <param name="Indices">Indices weiterer Dimensionen</param> ''' <value>zuzuweisender Wert</value> ''' <returns>abgefragter Wert</returns> Default Public Property Value(ByVal ind1 As Integer, _ ByVal ParamArray Indices() As Integer) As EntryType ' Nicht wundern: Da eine Standardeigenschaft nicht ' nur ein ParamArray als Parameter besitzen darf, ' ist der erste Array-Index 'ausgelagert' worden Get If IsNothing(gArray) Then Throw New NullReferenceException End If Return gArray(Get1D_Index(ind1, Indices)) End Get Set(ByVal value As EntryType) If IsNothing(gArray) Then Throw New NullReferenceException End If gArray(Get1D_Index(ind1, Indices)) = value End Set End Property
Private Function Get1D_Index(ByVal ind1 As Integer, _ ByVal ParamArray Indices() As Integer) As Integer ' Umrechnung der Indices auf das korrespondierende ' Element des internen eindimensionalen Arrays ' Indices auf Gültigkeit prüfen Dim i As Integer If UBound(Indices) <> UBound(gLimits) - 1 Then Throw New RankException End If If ind1 < gLimits(0).ug Or ind1 > gLimits(0).og Then Throw New IndexOutOfRangeException End If For i = 0 To UBound(Indices) If Indices(i) < gLimits(i + 1).ug Or _ Indices(i) > gLimits(i + 1).og Then Throw New IndexOutOfRangeException End If Next i ' Berechnung des 1D-Index unter Verwendung ' der dimensionalen Index-Multiplikatoren Dim ind As Integer = 0 '= ind1 * fak(0) ind = gDimFak(0) * (ind1 - gLimits(0).ug) For i = 1 To UBound(gLimits) ind += (gDimFak(i) * (Indices(i - 1) - gLimits(i).ug)) Next i Return ind End Function
End Class
Anwendungsbeispiel: ArrayXToY_Demo
Private Sub ArrayXtoY_Demo() ' Die generische Klasse 'ArrayXtoY erlaubt ' ' die Verwendung verschiedener Datentypen ' Erstellung Vier-dimensionaler Arrays: Dim str As New cArrayXToY(Of String) _ (-1, 5, -2, 6, -3, 7, -8, -4) Dim dbl As New cArrayXToY(Of Double) _ (-1, 5, -2, 6, -3, 7, -8, -4) Dim shrt As New cArrayXToY(Of Short) _ (-1, 5, -2, 6, -3, 7, -8, -4) Dim z As Integer z = -1 For i As Integer = str.Lbd(0) To str.Ubd(0) For k As Integer = str.Lbd(1) To str.Ubd(1) For l As Integer = str.Lbd(2) To str.Ubd(2) For m As Integer = str.Lbd(3) To str.Ubd(3) z += 1 ' Zuweisung auf akt. Array-Element str(i, k, l, m) = CStr(z) dbl(i, k, l, m) = z ' Erweiterungskonv. shrt(i, k, l, m) = CShort(z) ' expl. Konv. Next m Next l Next k Next i z = -1 For i As Integer = dbl.Lbd(0) To dbl.Ubd(0) For k As Integer = dbl.Lbd(1) To dbl.Ubd(1) For l As Integer = dbl.Lbd(2) To dbl.Ubd(2) For m As Integer = dbl.Lbd(3) To dbl.Ubd(3) z += 1 ' Prüfabfrage des akt. Array-Elements If str(i, k, l, m) <> CStr(z) Then Stop If dbl(i, k, l, m) <> z Then Stop If shrt(i, k, l, m) <> z Then Stop Next m Next l Next k Next i End Sub