2015-02-12 4 views
5

Ho bisogno di passare le struct come tipi di valore in un elenco. Supponiamo che io sono una struct che deriva da un'interfaccia:Aggiunta di Struct: interfaccia a un elenco <Interface>

interface IFoo 
{ 
    ... 
} 
struct Foo1 : IFoo { ... } 
struct Foo2 : IFoo { ... } 

//Some other class which contains this: 
List<IFoo> listOfFoo; 

So che se ho fatto in questo modo: IFoo foo = new Foo1(), si sarebbe trasformato in un valore di riferimento (pugilato).

  1. sarebbe la struct non essere passato come riferimento se ho aggiunto Foo1 o Foo2-List<IFoo>?
  2. In caso contrario, è sicuro fare List<object> e aggiungere solo queste strutture, o sarebbe meglio fare MemberwiseClone su una classe?

Sto anche cercando l'efficienza, in quanto ciò sarà per il rilevamento delle collisioni in una mappa di piastrelle.

+1

Una domanda di riferimento: http://stackoverflow.com/questions/63671/is-it-safe-for-struct-to-implement-interfaces?rq=1 .. E vorrei suggerire di andare con approccio di classe . –

risposta

2

Se si dispone di una lista in cui ho è un'interfaccia e si aggiungono elementi di tipo F1 e F2 dove entrambi implementano l'interfaccia ho, poi quando si recupera una delle due elementi dalla Lista puoi verificare il tipo di riferimento usando la parola chiave ** è ** e procedi ad applicare il cast corretto all'elemento che hai ottenuto dall'elenco.

Ad esempio:

struct Foo1 : IFoo {...} 
struct Foo2 : IFoo {...} 

List<IFoo> listOfFoo = new List<IFoo>(); 

IFoo foo1 = new Foo1(); 
IFoo foo2 = new Foo2(); 
listOfFoo.Add(foo1); 
listOfFoo.Add(foo2); 

// lets retrieve the first element and check if it's a Foo1 value type 
if(listOfFoo[0] is Foo1){ 
    // cast element from List to Foo1 
    Foo1 foo = (Foo1) listOfFoo[0]; 
} 

Dal momento che abbiamo a che fare con le strutture, quando l'elemento della lista viene colato indietro al tipo di valore originale, si dovrebbe essere unboxed. Ma troppi unboxing e boxing possono colpire le prestazioni e dal momento che si desidera eseguire qualcosa come il rilevamento delle collisioni, ciò potrebbe comportare prestazioni ridotte.

È obbligatorio per l'utente utilizzare le strutture? A causa delle modifiche apportate alle variabili di boxing, le variabili potrebbero non funzionare correttamente se si eseguono operazioni sugli oggetti box memorizzati e, come ho già detto, troppa boxe potrebbe portare a risultati scadenti.

A mio parere una classe con MemberwiseClone sarebbe meglio.

È possibile leggere l'articolo this su MSDN che descrive i pro e i contro di entrambe le strutture e le classi, questo potrebbe aiutare a capire meglio quando utilizzare l'uno o l'altro.

+0

Grazie per la risposta. Dal momento che ho più esperienza con MemberwiseClone(), ho deciso di andare con quello. –

5
  1. Le strutture dovrebbero essere in scatola. Perché non dovrebbero essere? Ogni valore in List<IFoo> deve essere un IFoo, quindi ogni istanza della struct aggiunta viene convertita, tramite il pugilato.

  2. Le strutture sono ancora in scatola perché object è anche un tipo di riferimento. Nel tuo scenario, non c'è semplicemente modo di evitare il pugilato a meno che tu dichiari che l'elenco sia di un tipo di valore specifico (List<Foo1> o List<Foo2>).

In generale, utilizzare le strutture per "efficienza" non è affatto una cosa semplice o ovvia. In particolare, semplicemente chucking in struct in cui altrimenti scrivere class non è garantito per rendere il vostro rendimento del codice migliore. Scrivi il tuo codice in modo ovvio prima (e ovvio qui significa: usando le classi), quindi determina (attraverso la profilazione) se hai bisogno di ottimizzarlo e se sì, come.

+0

Ottimo commento sull'ottimizzazione - Io secondo i tuoi pensieri. – naspinski

0

Ogni definizione di struttura crea in realtà due tipi di cose in .NET: un oggetto heap ("boxed") e un percorso di archiviazione ("unboxed"). Le posizioni di archiviazione di tipo interfaccia contengono riferimenti ad oggetti heap, quindi l'archiviazione di una struttura non condivisa in una variabile di tipo interfaccia richiederà che il suo contenuto venga copiato in un'istanza del tipo di oggetto heap.

È possibile per un parametro di tipo generico che è vincolato a un'interfaccia per identificare un tipo di posizione di memoria della struttura; uno può quindi invocare i metodi di interfaccia sul percorso di archiviazione in questione senza boxe. In alcuni casi, questo può offrire alcuni importanti vantaggi in termini di prestazioni. Sfortunatamente, non c'è modo di dire al compilatore "Non voglio che questa cosa sia inscatolata, e se faccio qualcosa che richiederebbe la boxe preferirei che il compilatore sgridasse piuttosto che inserire silenziosamente una conversione di boxe". Di conseguenza, è necessaria estrema attenzione quando si utilizzano strutture che implementano interfacce; se uno non è disposto a esercitare tale cura, è meglio avere tutte le strutture cercano di entrambi:

  • si comportano come oggetti (nel qual caso dovrebbe essere essere piccolo e impedire che eventuali metodi di mutazione diversa completa sostituzione) o

  • essere nient'altro che gruppi di variabili bloccate insieme con nastro adesivo (cioè un mucchio di campi pubblici).

Cose che implementano interfacce, al di fuori di alcuni casi particolari come IEquatable<T> il cui unico scopo centri in tutto le strutture, dovrebbero generalmente essere classi.