it-swarm.it

Quando dovrei usare uno struct invece di una classe?

MSDN dice che dovresti usare le strutture quando hai bisogno di oggetti leggeri. Ci sono altri scenari in cui una struttura è preferibile rispetto a una classe?

Alcune persone potrebbero aver dimenticato che:

  1. le strutture possono avere metodi.
  2. le strutture non possono essere ereditate.

Comprendo le differenze tecniche tra le strutture e le classi, semplicemente non ho una buona idea per quando usare una struttura.

295
Esteban Araya

MSDN ha la risposta: Scegliere tra classi e strutture .

Fondamentalmente, quella pagina ti dà un elenco di controllo di 4 elementi e dice di usare una classe a meno che il tuo tipo non soddisfi tutti i criteri.

Non definire una struttura a meno che il tipo non abbia tutte le seguenti caratteristiche:

  • Rappresenta logicamente un singolo valore, simile ai tipi primitivi (intero, doppio e così via).
  • Ha una dimensione dell'istanza inferiore a 16 byte.
  • È immutabile.
  • Non dovrà essere inscatolato frequentemente.
293
OwenP

Sono sorpreso di non aver letto nessuna delle risposte precedenti, che considero l'aspetto più cruciale:

Uso le strutture quando voglio un tipo senza identità. Ad esempio un punto 3D:

public struct ThreeDimensionalPoint
{
    public readonly int X, Y, Z;
    public ThreeDimensionalPoint(int x, int y, int z)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
    }

    public override string ToString()
    {
        return "(X=" + this.X + ", Y=" + this.Y + ", Z=" + this.Z + ")";
    }

    public override int GetHashCode()
    {
        return (this.X + 2) ^ (this.Y + 2) ^ (this.Z + 2);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ThreeDimensionalPoint))
            return false;
        ThreeDimensionalPoint other = (ThreeDimensionalPoint)obj;
        return this == other;
    }

    public static bool operator ==(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return p1.X == p2.X && p1.Y == p2.Y && p1.Z == p2.Z;
    }

    public static bool operator !=(ThreeDimensionalPoint p1, ThreeDimensionalPoint p2)
    {
        return !(p1 == p2);
    }
}

Se hai due istanze di questa struttura non ti importa se sono un singolo pezzo di dati in memoria o due. Ti preoccupi solo dei valori che detengono.

53
Andrei Rînea

Bill Wagner ha un capitolo su questo nel suo libro "efficace c #" ( http://www.Amazon.com/Effective-Specific-Ways-Improve-Your/dp/032124566 ). Conclude usando il seguente principio:

  1. La responsabilità principale del tipo è l'archiviazione dei dati?
  2. La sua interfaccia pubblica è definita interamente da proprietà che accedono o modificano i suoi membri di dati?
  3. Sei sicuro che il tuo tipo non avrà mai sottoclassi?
  4. Sei sicuro che il tuo tipo non verrà mai trattato polimorficamente?

Se rispondi "sì" a tutte e 4 le domande: usa una struttura. Altrimenti, usa una classe.

27
Bart Gijssens

Utilizzare uno struct quando si desidera la semantica del tipo di valore anziché il tipo di riferimento. Le strutture sono copia per valore, quindi fai attenzione!

Vedi anche le domande precedenti, ad es.

Qual è la differenza tra struct e class in .NET?

15
Simon Steele

Vorrei usare le strutture quando:

  1. si suppone che un oggetto sia di sola lettura (ogni volta che si passa/si assegna una struttura viene copiato). Gli oggetti di sola lettura sono fantastici quando si tratta di elaborazione multithread in quanto non richiedono il blocco nella maggior parte dei casi.

  2. un oggetto è piccolo e di breve durata. In tal caso, vi sono buone probabilità che l'oggetto venga allocato nello stack, il che è molto più efficiente rispetto a metterlo nell'heap gestito. Inoltre, la memoria allocata dall'oggetto verrà liberata non appena esce dal suo ambito. In altre parole è meno lavoro per Garbage Collector e la memoria viene utilizzata in modo più efficiente.

11
Pawel Pabich

Usa una classe se:

  • La sua identità è importante. Le strutture vengono copiate implicitamente quando vengono passate per valore in un metodo.
  • Avrà un ingombro di memoria elevato.
  • I suoi campi hanno bisogno di inizializzatori.
  • Devi ereditare da una classe base.
  • Hai bisogno di un comportamento polimorfico;

Usa una struttura se:

  • Funzionerà come un tipo primitivo (int, long, byte, ecc.).
  • Deve avere un ingombro di memoria ridotto.
  • Stai chiamando un metodo P/Invoke che richiede il passaggio di una struttura per valore.
  • È necessario ridurre l'impatto della garbage collection sulle prestazioni dell'applicazione.
  • I suoi campi devono essere inizializzati solo ai loro valori predefiniti. Questo valore sarebbe zero per i tipi numerici, false per i tipi booleani e null per i tipi di riferimento.
    • Si noti che in C # 6.0 le strutture possono avere un costruttore predefinito che può essere utilizzato per inizializzare i campi della struttura su valori non predefiniti.
  • Non è necessario ereditare da una classe di base (diversa da ValueType, da cui ereditano tutte le strutture).
  • Non è necessario un comportamento polimorfico.

Ho sempre usato uno struct quando volevo raggruppare alcuni valori per passare le cose da una chiamata di metodo, ma non avrò bisogno di usarlo per nulla dopo aver letto quei valori. Proprio come un modo per mantenere le cose pulite. Tendo a vedere le cose in una struttura come "usa e getta" e le cose in una classe come più utili e "funzionali"

5
Ryan Skarin

Se un'entità sarà immutabile, la questione se usare una struttura o una classe sarà generalmente una prestazione piuttosto che una semantica. Su un sistema a 32/64 bit, i riferimenti di classe richiedono 4/8 byte per l'archiviazione, indipendentemente dalla quantità di informazioni nella classe; la copia di un riferimento di classe richiederà la copia di 4/8 byte. D'altra parte, ogni distinto l'istanza di classe avrà 8/16 byte di overhead oltre alle informazioni in suo possesso e al costo della memoria dei riferimenti ad essa. Supponiamo che uno voglia un array di 500 entità, ognuna contenente quattro numeri interi a 32 bit. Se l'entità è un tipo di struttura, l'array richiederà 8.000 byte indipendentemente dal fatto che tutte e 500 le entità siano tutte identiche, tutte diverse o in qualche punto intermedio. Se l'entità è un tipo di classe, l'array di 500 riferimenti richiederà 4.000 byte. Se quei riferimenti puntano tutti a oggetti diversi, gli oggetti richiederebbero 24 byte ciascuno ciascuno (12.000 byte per tutti i 500), per un totale di 16.000 byte, il doppio del costo di archiviazione di un tipo di struttura. D'altra parte, del codice creato un'istanza di oggetto e quindi copiato un riferimento a tutti i 500 slot dell'array, il costo totale sarebbe di 24 byte per quell'istanza e 4.000 per l'array, per un totale di 4.024 byte. Un grande risparmio. Poche situazioni funzionerebbero così come l'ultima, ma in alcuni casi potrebbe essere possibile copiare alcuni riferimenti in sufficienti slot array per rendere utile tale condivisione.

Se si suppone che l'entità sia mutabile, la questione se utilizzare una classe o una struttura è in qualche modo più semplice. Supponiamo che "Thing" sia una struttura o una classe che ha un campo intero chiamato x, e si fa il seguente codice:

 Cosa t1, t2; 
 ... 
 T2 = t1; 
 T2.x = 5; 

Si vuole che quest'ultima affermazione influisca su t1.x?

Se Thing è un tipo di classe, t1 e t2 saranno equivalenti, nel senso che anche t1.xe t2.x saranno equivalenti. Pertanto, la seconda istruzione influenzerà t1.x. Se Thing è un tipo di struttura, t1 e t2 saranno istanze diverse, il che significa t1.xe t2.x si riferiranno a numeri interi diversi. Pertanto, la seconda istruzione non influirà su t1.x.

Le strutture mutabili e le classi mutabili hanno comportamenti sostanzialmente diversi, sebbene .net abbia alcune stranezze nella gestione delle mutazioni di struttura. Se si desidera un comportamento di tipo valore (nel senso che "t2 = t1" copierà i dati da t1 a t2 lasciando t1 e t2 come istanze distinte), e se si può vivere con le stranezze nella gestione dei tipi di valore di .net, usare una struttura. Se si desidera una semantica di tipo valore ma le stranezze di .net porterebbero a una semantica di tipo valore errata nella propria applicazione, utilizzare una classe e borbottare.

4
supercat

Inoltre le eccellenti risposte sopra:

Le strutture sono tipi di valore.

Non possono mai essere impostati su Nothing.

Impostando una struttura = Nothing, imposterai tutti i tipi di valori sui valori predefiniti.

3

quando non hai davvero bisogno di comportamento, ma hai bisogno di più struttura di un semplice array o dizionario.

Follow up Ecco come penso alle strutture in generale. So che possono avere metodi, ma mi piace mantenere quella distinzione mentale generale.

2
Jim Deville

Come diceva @Simon, le strutture forniscono una semantica di "tipo valore", quindi se hai bisogno di un comportamento simile a un tipo di dati incorporato, usa una struttura. Poiché le strutture vengono passate per copia, si desidera assicurarsi che siano di piccole dimensioni, circa 16 byte.

2
Scott Dorman

Le strutture sono sullo Stack, non sull'Heap, quindi sono thread-safe e dovrebbero essere utilizzate durante l'implementazione del modello di oggetti di trasferimento, non si desidera mai usare oggetti sull'Heap ma sono volatili, in questo caso si desidera utilizzare lo Stack di chiamate, questo è un caso di base per l'utilizzo di una struttura che mi sorprende da tutte le risposte di uscita qui,

1
Jack

Hmm ...

Non userei la garbage collection come argomento a favore/contro l'uso di struct vs class. L'heap gestito funziona in modo molto simile a uno stack: la creazione di un oggetto lo mette in cima all'heap, che è quasi altrettanto veloce dell'allocazione sullo stack. Inoltre, se un oggetto ha una vita breve e non sopravvive a un ciclo GC, la deallocazione è gratuita poiché il GC funziona solo con memoria ancora accessibile. (Cerca MSDN, c'è una serie di articoli sulla gestione della memoria .NET, sono troppo pigro per andare a scavare per loro).

La maggior parte delle volte uso una struttura, finisco per prendermi a calci per farlo, perché in seguito scopro che avere una semantica di riferimento avrebbe reso le cose un po 'più semplici.

Ad ogni modo, quei quattro punti nell'articolo MSDN pubblicato sopra sembrano una buona linea guida.

1
KG