Accesso a un array privato quando ho un metodo di estensione

2

Sto provando a creare un certo numero di classi di dati per il mio gioco C # / XNA, che sarebbe essenzialmente un wrapper attorno a T[,] :

public interface IGrid<T>
    where T : struct
{
    Point Size { get; }
    T this[int x, int y] { get; set; }
}

Ad esempio, potrei avere un oggetto Heightmap che è essenzialmente un IGrid<float> , oppure potrei avere un oggetto Level.TileData che è un IGrid<byte> o IGrid<int> . Queste potrebbero essere classi completamente separate con specifici metodi di supporto, ecc.

Ho realizzato anche una piccola libreria di estensioni che opera specificamente su qualsiasi array bidimensionale:

public static class Transformations
{
    static T[,] Transpose(this T[,] value);
    static T[,] FlipHorizontal(this T[,] value);
    static T[,] FlipVertical(this T[,] value);
    static T[,] RotateClockwise(this T[,] value);
    static T[,] RotateCounterClockwise(this T[,] value);
}

Tuttavia, quando implemento effettivamente IGrid<T> , ho riscontrato un piccolo problema di progettazione.

Poiché l'array di backup dovrebbe essere privato / protetto, qualsiasi codice client che effettivamente utilizza IGrid<T> non sarebbe in grado di utilizzare la mia libreria di estensioni.

In base a CA1819 e blog di Eric's , esponendo pubblicamente un array (anche tramite le proprietà!) è una cattiva idea, in quanto sarebbe completamente modificabile. La soluzione suggerita è quella di clonare l'array, quindi esporlo come un tipo immutabile (il che significa che non ho ancora una matrice su cui utilizzare la libreria).

Sto pensando troppo a qualcosa, o c'è una soluzione elegante qui? Mi piace molto l'idea di usare gli indicizzatori per leggere / scrivere i dati sull'oggetto, ma in alcune situazioni I potrebbe voler lavorare con l'intero array in una sola volta.

    
posta Kyle Baran 19.04.2014 - 02:32
fonte

1 risposta

0

Non è garantito che un client che implementa IGrid<T> utilizzi effettivamente un array 2D T[,] per l'implementazione. Quindi, senza cambiare qualcosa, non puoi aspettarti di far funzionare la tua libreria di estensioni "out of the box" su IGrid<T> . IMHO hai le seguenti opzioni:

  • modifica (o estendi) l'interfaccia della tua classe Transformation per utilizzare IGrid<T> come input anziché T[,] . Questa è probabilmente l'alternativa più pulita, ma suppongo che se vuoi mantenere e mantenere la variante T[,] , significherà anche una duplicazione del codice nella tua libreria di estensioni, poiché T[,] e IGrid<T> non hanno una comune interfaccia.

  • per evitare la duplicazione del codice, potresti scrivere un metodo di conversione da IGrid<T> a T[,] , ma questo avrà lo svantaggio di creare copie aggiuntive dei dati. Potresti usare questo con il mio primo suggerimento per creare i metodi dell'adattatore per evitare la duplicazione del codice:

     T[,] Transpose(this IGrid<T> value)
     {
          return ConvertIGridToArray(value).Transpose();
     }
    
  • estendi IGrid<T> per fornire l'accesso al T[,] sottostante, con lo svantaggio di perdere l'immutabilità (che obbliga qualsiasi client a utilizzare internamente un T[,] )

Al momento, non vedo un modo per evitare la duplicazione del codice e la copia aggiuntiva e della perdita di immutabilità tutte e tre - scegli la tua scelta.

Si noti che per i grandi array, l'immutabilità potrebbe non essere la progettazione ottimale per le operazioni di trasformazione, senza immutabilità potrebbero essere implementate sul posto, che a volte è l'alternativa migliore, poiché consuma meno memoria. Nota inoltre che se vuoi che i tuoi metodi di trasformazione funzionino su qualcosa come una classe Heightmap , non possono consegnare direttamente un nuovo oggetto Heightmap , dovrai anche fare una conversione di tipo. Una trasformazione sul posto non avrebbe questo problema, potrebbe operare direttamente sul T[,] interno dell'oggetto Heightmap.

    
risposta data 19.04.2014 - 07:01
fonte

Leggi altre domande sui tag