vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
TOP-Angebot: 17 bzw. 24 Entwickler-Vollversionen zum unschlagbaren Preis!  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2024
 
zurück
Rubrik: Variablen/Strings · Array/ArrayList   |   VB-Versionen: VB2005, VB200808.05.08
Array.Copy bei mehrdimensionalen Arrays

Berechnung des Start- und Zielindex für die Copy-Methode bei mehrdimensionalen Arrays

Autor:   Manfred BohnBewertung:     [ Jetzt bewerten ]Views:  13.731 
ohne HomepageSystem:  WinNT, Win2k, WinXP, Win7, Win8, Win10, Win11 Beispielprojekt auf CD 

Die Klasse 'System.Array' ist die Basisklasse für alle Arrays in der Common Language Runtime. Sie enthält mehrere Überladungen der 'Copy'-Methode, durch die eine Anzahl von Elementen zwischen Instanzen der Array-Klasse kopiert werden kann - und zwar einschließlich 'Type-Casting' und 'Boxing'.

Beim Kopieren ist es deshalb nicht notwendig, dass die Elemente des Quell-Array und des Ziel-Array vom gleichen Datentyp sind. Zulässige Erweiterungskonvertierungen sind möglich. Durch einen Aufruf kann beim Kopieren z.B. der Inhalt eines Short-Array in ein Long-Array oder die Elemente eines Integer-Array können in ein Double-Array eingetragen werden.

Dieser Tipp bezieht sich auf die Überladung der 'Copy'-Methode, deren Parameterliste einen SourceIndex und einen DestinationIndex umfasst:

Public Shared Sub Copy (sourceArray As Array, _
  sourceIndex As Long, _
  destinationArray As Array, _
  destinationIndex As Long, _
  length As Long)

Bei einem eindimensionalen Array ist der Fall klar. Man muss nur darauf achten, dass das Quell-Array ab dem Startindex genügend Elemente umfasst (mindestens die Anzahl, die sich aus der Angabe im Length-Parameter ergibt). Und auch im Ziel-Array müssen genügend Elemente ab dem 'Destinationindex’ deklariert sein. (Die Methode verändert die Array-Deklarationen nicht; sie führt keine Anpassung des Zielarray durch, sondern löst ggf. eine Ausnahme aus.) Das Quell- und das Ziel-Array können identisch sein, die durch die Parameter angegebenen Array-Abschnitte dürfen sich sogar überlappen.

Aber was ist bei mehrdimensionalen Arrays?
Die Array.Copy-Methode behandelt alle Arrays prinzipiell als eine einfache 'Datenschlange'. Und in der hat jedes Element einen eindeutigen Index - auch bei mehrdimensionalen Arrays.

Allerdings ist bei mehrdimensionalen Arrays zu beachten, dass der Rank (=die Zahl der Dimensionen) von Quell- und Ziel-Array identisch sein müssen. Es spielt jedoch keine Rolle, wie die Obergrenzen der einzelnen Dimensionen vereinbart worden sind. Die Copy-Methode berücksichtigt die mehrdimensionale Indizierung nicht, sondern kopiert einfach einen Abschnitt der Quell-Datenschlange in einen Abschnitt des Ziel-Array. (Die korrespondierenden Elemente des Quell- und Zielarray weisen deshalb nach dem Kopieren keine Entsprechung in der Indizierung auf - falls unterschiedliche Deklarations-Obergrenzen vorliegen. Zu einer 'Ausnahme'führt dieser Fall nicht!)

Die unten angegebene Methode 'Get1DArrayIndex' ermittelt zu einem Array und einem Satz Indices, die sich auf ein Array-Element beziehen, den für 'Copy' benötigten Index in der Datenschlange. Es müssen so viele (gültige) Indizes angegeben werden (ParamArray) wie das Array Dimensionen aufweist. Falls die Routine scheitert, gibt sie den Wert -1 zurück.

Die Methode ArrayCopyTo erwartet ein Quell-Array und ein Ziel-Array, die Angabe der Zahl der zu kopierenden Elemente und einen Satz Indices, aus denen der Startindex im QUELL-Array für den Kopier-Vorgang berechnet wird.

Quell- und Ziel-Array müssen sich in den Datentypen entsprechen (Erweiterungskonvertierung Quelle -> Ziel) und die gleiche Zahl von Dimensionen aufweisen.

Die Elemente werden im Zielarray ab Position 0 angeordnet.

Die Methode ArrayCopyFrom erwartet ein Quell-Array und ein Ziel-Array, die Angabe der Zahl der zu kopierenden Elemente und einen Satz Indices, aus denen der Startindex im ZIEL-Array für den Kopier-Vorgang berechnet wird. Quell- und Zielarray müssen sich in den Datentypen entsprechen (Erweiterungskonvertierung Quelle -> Ziel) und die gleiche Zahl von Dimensionen aufweisen. Die Elemente werden aus dem Quellarray ab Position 0 kopiert.

Falls das Kopieren scheitert, geben diese Funktionen 'false' zurück. Die Arrays werden von den Funktionen als 'System.Array' erwartet. Aus diesem Grund können Arrays beliebiger Dimensionszahl und beliebiger Datentypen übergeben werden. Allerdings darf 'Option Strict On' nicht eingestellt worden sein. Wer das nicht möchte, sollte jeweils eine Überladung für die erforderliche Zahl von Dimensionen und die verwendeten Datentypen programmieren.

In der Test-Routine 'TestArrayCopy' wird zunächst ein vierdimensionales Array so mit Werten gefüllt, dass eine Identifikation der Array-Indices aus dem Array-Inhalt möglich ist.

In einer Testschleife wird jeweils ein zufällig ausgewähltes Arrayelement in ein ein-elementiges (aber vier-dimensionales) Zielarray kopiert (ArrayCopyTo) und danach an die quell-entsprechende Position in einem zweiten Ziel-Array weitergereicht (ArrayCopyFrom).

Dabei wird zugleich eine Datentyp-Konvertierung durchgeführt.

Im dritten Schleifen-Block wird demonstriert, wie sich der eindimensionale Array-Index aus der Array-Indizierung ergibt.

Hinweis für VB6-Umsteiger:
Die Methode Array.Copy ersetzt - auf die hier gezeigte Weise - die Tricks, die sonst mit der API-Funktion 'CopyMemory' durchgeführt worden sind, um Array-Abschnitte zu übertragen.

  ''' <summary>
  ''' Kopiert Arrayelemente ab Startindizierung in Zielarray 
  ''' (ab Startposition 0)
  ''' </summary>
  ''' <param name="qarr">Quell-Array</param>
  ''' <param name="zarr">Ziel-Array</param>
  ''' <param name="ElementZahl">Anzahl der zu kopierenden 
  ''' Array-Elemente</param>
  ''' <param name="StartIndices">Indizes des ersten zu kopierenden 
  ''' Elements im Quell-Array</param>
  ''' <returns>True, falls erfolgreich</returns>
  Public Function ArrayCopyTo(ByVal qarr As System.Array, _
    ByRef zarr As System.Array, _
    ByVal ElementZahl As Integer, _
    ByVal ParamArray StartIndices() As Integer) As Boolean
 
    ' Das Zielarray wird 'ByRef' übergeben und ist als
    ' System.Array deklariert (wie Quelle), damit Arrays
    ' beliebiger Dimensionen und beliebiger Datentypen 
    ' übergeben werden können
    ' ---> 'Option Strict Off' ist leider erforderlich
 
    ' Enthalten die Array-Parameter Nullverweise?
    If IsNothing(qarr) Or IsNothing(zarr) Then Return False
 
    ' Stimmt die Dimensionszahl Quelle/Ziel überein?
    If qarr.Rank <> zarr.Rank Then Return False
 
    ' 1D-Index zur StartIndizierung (Quelle) ermitteln
    Dim StartIndex As Long = Get1DArrayIndex(qarr, StartIndices)
 
    ' Index ermittelt ? 
    If StartIndex < 0 Then Return False
 
    ' Ausreichende Zahl von Elementen im Zielarray??
    If zarr.Length < ElementZahl Then Return False
 
    Try
      ' Kopiervorgang durchführen
      Array.Copy(qarr, StartIndex, zarr, 0, ElementZahl)
      Return True
    Catch ex As Exception
      ' z.B. falls zu wenige (Rest-) Elemente im Quell-Array
      ' oder falls die Datentypen nicht zusammenpassen
      Return False
    End Try
  End Function
  ''' <summary>
  ''' Kopiert Arrayelemente ab 0 in Zielarray (gemäß     
  ''' Start-Indizierung)
  ''' </summary>
  ''' <param name="qarr">Quell-Array</param>
  ''' <param name="zarr">Ziel-Array</param>
  ''' <param name="ElementZahl">Anzahl der zu kopierenden 
  ''' Array-Elemente</param>
  ''' <param name="StartIndices">Indizes des ersten zu 
  ''' kopierenden Elements im Ziel-Array</param>
  ''' <returns>True, falls erfolgreich</returns>
  Public Function ArrayCopyFrom(ByVal qarr As System.Array, _
    ByRef zarr As System.Array, _
    ByVal ElementZahl As Integer, _
    ByVal ParamArray StartIndices() As Integer) As Boolean
 
    ' Stimmt die Dimensionszahl Quelle/Ziel überein?
    If qarr.Rank <> zarr.Rank Then Return False
 
    ' 1D-Index zur gegebenen Start-Indizierung (Ziel) ermitteln
    Dim StartIndex As Long = Get1DArrayIndex(zarr, StartIndices)
    ' 1D-Start-Index ermittelt ? 
    If StartIndex < 0 Then Return False
 
    ' Ausreichende Zahl von Elementen im Quellarray??
    If qarr.Length < ElementZahl Then Return False
 
    Try
      ' Kopiervorgang durchführen
      Array.Copy(qarr, 0, zarr, StartIndex, ElementZahl)
      Return True
    Catch
      ' z.B. falls zu wenige (Rest-) Elemente im Ziel-Array
      ' oder bei inkompatiblen Datentypen 
      Return False
    End Try
  End Function
  Private Function Get1DArrayIndex(ByVal arr As System.Array, _
    ByVal ParamArray StartIndices() As Integer) As Long
 
    ' Die Funktion ermittelt zu einem beliebig dimensionierten 
    ' System.Array und der gegebenen Indizierung eines Elements 
    ' den korrespondierenden 1D-Index in der 'Datenschlange' 
 
    ' Falls die Indizierung nicht stimmt, wird -1 zurückgegeben
 
    Dim i, k As Integer            ' Loops
    Dim ind As Long                ' Rückgabe
    Dim fak() As Long              ' Dim-Faktoren
 
    ' Zahl der übergebenen Indices = Rank des QuallArray ??
    If UBound(StartIndices) + 1 <> arr.Rank Then Return -1
 
    ' Gültige Indizierung?
    For i = 0 To UBound(StartIndices)
      If StartIndices(i) < 0 Or _
      StartIndices(i) > UBound(arr, i + 1) Then Return -1
    Next
 
    ReDim fak(arr.Rank)
 
    ' Berechnung des entsprechenden 1D-Start-Index für den 
    ' Aufruf der Array.Copy-Methode
 
    ' Zu überspringende Elementzahl pro Indexwert einer Dimension
    ' => Multiplikation der Elementzahl aller höheren Dimensionen
    For i = 0 To arr.Rank - 1
      fak(i) = 1
      For k = i + 1 To arr.Rank - 1
        fak(i) *= arr.GetLength(k)
      Next k
    Next i
 
    ' Berechnung des 1D-Startindex unter Verwendung 
    ' der Index-Multiplikatoren
    ind = 0
    For i = 0 To arr.Rank - 1
      ind += fak(i) * (StartIndices(i))
    Next i
 
    ' 1D-Index zurückgeben
    Return ind
  End Function
  Private Sub Test_ArrayCopy()
    Dim i, k, l, m As Integer           ' Schleifen-Indizes
    Dim ind, zz As Integer              ' für Demos
    Dim qarr(9, 19, 29, 39) As Integer  ' Quell-Array
    Dim zarr1(0, 0, 0, 0) As Integer    ' Ziel-Array für 1 Element 
    Dim zarr2(9, 19, 29, 39) As Double  ' Ziel-Array 
 
    ' Vierdimensionales Array mit Daten füllen, aus deren 
    ' Wert sich die zugehörigen Array-Indices ermitteln lassen
    For i = 0 To UBound(qarr, 1)
      For k = 0 To UBound(qarr, 2)
        For l = 0 To UBound(qarr, 3)
          For m = 0 To UBound(qarr, 4)
            ' Array mit Werten füllen, die verraten, 
            ' welche Indizierung das Element ursprünglich 
            ' besitzt
            qarr(i, k, l, m) = _
            i * 10000000 + k * 100000 + l * 1000 + m
          Next m
        Next l
      Next k
    Next i
 
    ' 10000 Kopier-Testdurchläufe
    For zz = 1 To 10000
      ' zufällige Indizierung 
      i = CInt(Rnd() * UBound(qarr, 1))
      k = CInt(Rnd() * UBound(qarr, 2))
      l = CInt(Rnd() * UBound(qarr, 3))
      m = CInt(Rnd() * UBound(qarr, 4))
 
      ' Ein Array-Element in das Zielarray (1) kopieren
      If Not ArrayCopyTo(qarr, zarr1, 1, i, k, l, m) Then Stop
 
      ' Ist das angeforderte Element korrekt kopiert worden? 
      If zarr1(0, 0, 0, 0) <> _
      i * 10000000 + k * 100000 + l * 1000 + m Then Stop
 
      ' Ein Array-Element in das Zielarray (2) an die 
      ' Indizierte Stelle kopieren
      If Not ArrayCopyFrom(zarr1, zarr2, 1, i, k, l, m) Then Stop
 
      'Steht das kopierte Element im Zielarray an der
      ' korrekten Stelle? (Vergleich mit Quell-Array-Element)
      If zarr2(i, k, l, m) <> qarr(i, k, l, m) Then Stop
 
      zarr2(i, k, l, m) = 0  ' Zielarray wieder 'säubern'
    Next zz
 
    ' Demonstration, wie die Arrayelemente als 'Datenschlange'
    ' angeordnet sind (die höchste Dimension läuft zuerst)
    zz = -1
    For i = 0 To UBound(qarr, 1)
      For k = 0 To UBound(qarr, 2)
        For l = 0 To UBound(qarr, 3)
          For m = 0 To UBound(qarr, 4)
            ' 1D-Index zur Indizierung besorgen
            ind = Get1DArrayIndex(qarr, i, k, l, m)
            ' Zählvariable hochsetzen
            zz += 1
            ' Der 1D-Index läuft mit der Zählvariable hoch 
            If ind <> zz Then Stop
          Next m
        Next l
      Next k
    Next i
  End Sub

Dieser Tipp wurde bereits 13.731 mal aufgerufen.

Voriger Tipp   |   Zufälliger Tipp   |   Nächster Tipp

Über diesen Tipp im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Tipp, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Neue Diskussion eröffnen

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Tipps & Tricks finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2024 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel