2010-08-04 6 views
5

Ich muss über ein Array beliebiger Rangfolge iterieren. Dies gilt sowohl für das Lesen als auch für das Schreiben, so dass GetEnumerator nicht funktioniert.Indizierung in Arrays mit beliebigem Rang in C#

funktioniert nicht auf mehrdimensionalen Arrays. Array.SetValue(object, params int[]) würde übermäßige Arithmetik für die Iteration durch den multidimensionalen Raum erfordern. Es würde auch einen dynamischen Aufruf erfordern, um den params Teil der Signatur zu umgehen.

Ich bin versucht, das Array zu pinnen und mit einem Zeiger darüber zu iterieren, aber ich kann keine Dokumentation finden, die besagt, dass mehrdimensionale Arrays garantiert zusammenhängend sind. Wenn sie am Ende einer Dimension aufgefüllt sind, funktioniert das nicht. Ich würde es auch vorziehen, unsicheren Code zu vermeiden.

Gibt es eine einfache Möglichkeit, ein mehrdimensionales Array mit nur einem einzigen Index sequenziell zu adressieren?

+2

Ich sehe keinen Fehler bei der Verwendung von "unsicheren" Code. Es ist sicher, Zeiger zu verwenden, wenn Sie tatsächlich wissen, was Sie tun, und es ist eine praktikable und effiziente Lösung. –

+2

Btw es ist vollkommen gültig, ein 'int []' zu übergeben, wo ein 'params int []' erwartet wird. – dtb

+0

@ dtb, scheinen Sie an beiden Syntaxpunkten korrekt zu sein. Ich wusste nie, dass Sie ein Array direkt an einen params-Array-Parameter übergeben können. –

Antwort

5

Mehrdimensionale Arrays sind garantiert zusammenhängend. Von ECMA-335:

Array-Elemente innerhalb des Arrays Objekts angelegt in Zeilenhauptordnung (dh verbunden, um die Elemente mit der am weitesten rechts liegenden Felddimension sollen ausgelegt werden aneinander angrenzend vom niedrigsten zum höchsten Index) werden sollen .

So funktioniert das:

int[,,,] array = new int[10, 10, 10, 10]; 

fixed (int* ptr = array) 
{ 
    ptr[10] = 42; 
} 

int result = array[0, 0, 1, 0]; // == 42 
+0

Danke, das ist, was ich gesucht habe und gibt mir eine gute Fallback-Implementierung. BTW, ich denke, dass Ihr Code auf 'int * ptr = Array' erstickt, aber die Idee ist gesund. –

+0

@Kennet Belenky: Ich habe nur überprüft, und der Code funktioniert perfekt so wie es ist. Natürlich hilft eine Lösung mit unsicherem Code nicht, wenn Sie unsicheren Code vermeiden wollen, aber ich befürchte, dass es keine andere Lösung gibt, die nicht mit (unnötigem) Overhead einhergeht (z. B. rekursives Durchlaufen aller Dimensionen). . – dtb

+0

... es stellt sich jedoch heraus, dass das Pinning nur funktioniert, wenn das Array primitive Werttypen enthält. Ich habe dies in der ursprünglichen Frage nicht erwähnt, aber das Array, das ich ansprechen möchte, hat auch einen beliebigen ElementType und kann Strukturen, Referenztypen oder umrahmte Werttypen enthalten. Ich denke, ich muss auf eine Indexierungsmatrix zurückgreifen. –

-1

Vielleicht könnten Sie sie alle in einer einzigen temporären Sammlung verbinden, und nur darüber iterieren.

+0

Einfach genug, ich werde GetEnumerator verwenden, um alles im Array zu adressieren. Das Problem ist, dass ich auch an verschiedenen Punkten im Array schreiben muss, und GetEnumerator wird dafür fehlschlagen. –

1

Sie die Rank und GetUpperBound Eigenschaft/Methode verwenden, um einen Indizes Array zu erstellen, die Sie zu dem wichtigsten Array SetValue und GetValue Methoden übergeben können:

int[] Indices(Array a, int idx) 
{ 
    var indices = new int[a.Rank]; 

    for (var i = 0; i < a.Rank; i++) 
    { 
     var div = 1; 

     for (var j = i + 1; j < a.Rank; j++) 
     { 
      div *= a.GetLength(j); 
     } 

     indices[i] = a.GetLowerBound(i) + idx/div % a.GetLength(i); 
    } 

    return indices; 
} 

..und verwenden Sie es so:

for (var i = 0; i < array.Length; i++) 
{ 
    var indices = Indices(array, i); 
    array.SetValue(i, indices); 
    var val = array.GetValue(indices); 
} 
+1

Ja, das wäre die exzessive Arithmetik, von der ich gesprochen habe. Sie müssten auch Array.GetLowerBound und nicht nur Array.GetUpperBound verwenden. –

+0

Sie haben absolut Recht, setzen Sie es direkt in den Code, sorry. –

+0

@kennet Eigentlich brauchen wir * Array.GetUpperBound' nicht, nur die 'Array.GetLength' Methode :) –