È possibile convertire l'array C# double[,,] in double[] senza farne una copia

StackOverflow https://stackoverflow.com/questions/96054

  •  01-07-2019
  •  | 
  •  

Domanda

Ho enormi matrici 3D di numeri nella mia applicazione .NET.Devo convertirli in un array 1D per passarlo a una libreria COM.C'è un modo per convertire l'array senza fare una copia di tutti i dati?

Posso eseguire la conversione in questo modo, ma utilizzo il doppio della quantità di memoria che rappresenta un problema nella mia applicazione:

  double[] result = new double[input.GetLength(0) * input.GetLength(1) * input.GetLength(2)];
  for (i = 0; i < input.GetLength(0); i++) 
    for (j = 0; j < input.GetLength(1); j++) 
      for (k = 0; k < input.GetLength(2); k++) 
        result[i * input.GetLength(1) * input.GetLength(2) + j * input.GetLength(2) + k)] = input[i,j,l];
  return result;
È stato utile?

Soluzione

Non credo che il modo in cui C# memorizza i dati in memoria lo renderebbe fattibile allo stesso modo di un semplice cast in C.Perché non utilizzare un array 1d per cominciare e magari creare una classe per il tipo in modo da potervi accedere nel programma come se fosse un array 3d?

Altri suggerimenti

Sfortunatamente, non è garantito che gli array C# siano nella memoria contigua come lo sono in linguaggi più vicini al metal come C.Quindi no.Non c'è modo di convertire double[,,] in double[] senza una copia elemento per elemento.

Considera l'idea di astrarre l'accesso ai dati con a Procura (simile agli iteratori/puntatori intelligenti in C++).Sfortunatamente, la sintassi non è pulita come il C++ poiché operator() non è disponibile per l'overload e operator[] è ad argomento singolo, ma comunque vicino.

Naturalmente, questo ulteriore livello di astrazione aggiunge complessità e lavoro di per sé, ma ti consentirebbe di apportare modifiche minime al codice esistente che utilizza oggetti double[,,], consentendoti al tempo stesso di utilizzare un singolo array double[] per entrambi l'interoperabilità e il calcolo in C#.

class Matrix3
{
    // referece-to-element object
    public struct Matrix3Elem{
        private Matrix3Impl impl;
        private uint dim0, dim1, dim2;
        // other constructors
        Matrix3Elem(Matrix3Impl impl_, uint dim0_, uint dim1_, uint dim2_) {
            impl = impl_; dim0 = dim0_; dim1 = dim1_; dim2 = dim2_;
        }
        public double Value{
            get { return impl.GetAt(dim0,dim1,dim2); }
            set { impl.SetAt(dim0, dim1, dim2, value); }
        }
    }

    // implementation object
    internal class Matrix3Impl
    {
        private double[] data;
        uint dsize0, dsize1, dsize2; // dimension sizes
        // .. Resize() 
        public double GetAt(uint dim0, uint dim1, uint dim2) {
            // .. check bounds
            return data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ];
        }
        public void SetAt(uint dim0, uint dim1, uint dim2, double value) {
            // .. check bounds
            data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ] = value;
        }
    }

    private Matrix3Impl impl;

    public Matrix3Elem Elem(uint dim0, uint dim1, uint dim2){
        return new Matrix2Elem(dim0, dim1, dim2);
    }
    // .. Resize
    // .. GetLength0(), GetLength1(), GetLength1()
}

E quindi utilizzando questo tipo sia per leggere che per scrivere: 'foo[1,2,3]' è ora scritto come 'foo.Elem(1,2,3).Value', sia nella lettura dei valori che nella scrittura dei valori, su lato sinistro dell'assegnazione e delle espressioni di valore.

void normalize(Matrix3 m){

    double s = 0;
    for (i = 0; i < input.GetLength0; i++)     
        for (j = 0; j < input.GetLength(1); j++)       
            for (k = 0; k < input.GetLength(2); k++)
    {
        s += m.Elem(i,j,k).Value;
    }
    for (i = 0; i < input.GetLength0; i++)     
        for (j = 0; j < input.GetLength(1); j++)       
            for (k = 0; k < input.GetLength(2); k++)
    {
        m.Elem(i,j,k).Value /= s;
    }
}

Anche in questo caso, sono stati aggiunti costi di sviluppo, ma sono stati condivisi i dati, eliminando le spese generali di copia e i relativi costi di sviluppo.È un compromesso.

Come soluzione alternativa potresti creare una classe che mantenga l'array in una forma unidimensionale (magari anche più vicina alla forma bare metal in modo da poterla passare facilmente alla libreria COM?) e quindi sovraccaricare operator[] su questa classe per renderla utilizzabile come array multidimensionale nel codice C#.

Senza conoscere i dettagli della tua libreria COM, cercherei di creare una classe di facciata in .Net ed esporla a COM, se necessario.
La tua facciata richiederebbe un double[,,] e avrebbe un indicizzatore che mapperà da [] a [,,].

Modificare:Sono d'accordo sui punti sollevati nei commenti, il suggerimento di Lorens è migliore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top