Se tipo Foo
implementa IBar
, ci sono due modi con cui i membri della IBar
possono essere utilizzati in un'istanza di Foo
:
Un riferimento all'istanza (per tipi di classe) oppure un riferimento un oggetto heap contenente una copia dell'istanza (per i tipi di valore) può essere archiviato in una variabile di tipo IBar
L'istanza (per i tipi di valore) o un riferimento ad essa (per i tipi di classe) possono essere memorizzati in una variabile (o campo, matrice e lement o altra posizione di archiviazione) di un tipo generico vincolato a IBar
.
Una posizione di un tipo di classe o interfaccia tipo stoccaggio terrà sia un riferimento a un oggetto mucchio, o null
. Una posizione di memorizzazione di un tipo di valore primitivo contiene una raccolta di bit che rappresentano il suo valore. Una posizione di memorizzazione di un tipo di valore non primitivo contiene i contenuti concatenati di tutti i suoi campi di istanza pubblici e privati. Una posizione di archiviazione di tipo generico T
contiene un riferimento di heap se T
è un tipo di classe o un tipo di interfaccia, una raccolta di bit che rappresenta un valore primitivo se T
è un tipo di valore primitivo o i valori concatenati di un campo se T
è un tipo di struttura; questa determinazione si basa sul tipo effettivo di T
e non su nessuno dei suoi vincoli.
Tornando ai tipi Foo
e IBar
, memorizzando un Foo
in IBar
farà sì che il sistema per creare un nuovo oggetto mucchio che conterrà il contenuto concatenato di tutti i campi pubblici e privati di Foo
, e memorizzare un riferimento a quel mucchio oggetto in IBar
. Questo oggetto heap si comporterà come come qualsiasi altro oggetto heap, anziché come un percorso di memorizzazione di tipo valore. Avere cose che a volte si comportano come tipi di valore e, a volte, come tipi di classi possono creare confusione; è meglio evitare una semantica mista quando possibile.
Il tempo principale può essere utile per una struttura che implementa un'interfaccia nei casi in cui l'utilizzo seguirà il secondo schema sopra riportato. Ad esempio, si potrebbe avere un metodo:
// Return 1, 2, or 3 depending upon how many matching or different things there are
int CountUniqueThings<T>(T first, T second, T third) where T:IEquatable<T>
In tale situazione, se si dovesse chiamare CountUniqueThings(4, 6, 6)
, il sistema potrebbe invocare il metodo IEquatable<System.Int32>.Equals(System.Int32)
direttamente sui parametri passati-in di tipo System.Int32
. Se il metodo era stato invece dichiarato:
int CountUniqueThings(IEquatable<System.Int32> first,
IEquatable<System.Int32> second,
IEquatable<System.Int32> third)
poi il chiamante avrebbe dovuto creare un nuovo oggetto mucchio per mantenere il valore 4 Int32
, un secondo per mantenere il valore 6, una terza che potrebbe anche contenere il valore 6 e dovrebbe quindi passare i riferimenti a tali oggetti al metodo CountUniqueThings
. Icky.
La creazione di oggetti heap ha un costo certo. Se si crea qualcosa di un tipo di valore si evita la necessità di creare oggetti heap per contenere la maggior parte delle istanze, questa può essere una grande vittoria. Se ogni istanza creata creerebbe la creazione di un oggetto heap per contenere una copia, tuttavia (ad esempio per l'assegnazione a una variabile di tipo interfaccia), il vantaggio di tipo valore si annullerebbe completamente. Se le istanze richiederebbero in media la creazione di più di un oggetto heap per conservare le copie (ogni volta che un tipo viene convertito dal tipo di heap al tipo di valore e viceversa, sarà richiesta una nuova istanza), l'utilizzo di tipi di valore potrebbe essere molto inferiore efficiente rispetto alla semplice costruzione di un'istanza di tipo classe a cui il codice potrebbe passare attorno a un riferimento.
Come nota, fare attenzione a credere/perpetuare le semplificazioni strette dei tipi di valore e dello stack. http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx –
Personalmente non userei affatto un'interfaccia per un tipo immutabile con valore semantica. Per i principianti, è impossibile garantire al codice * client * che i valori nel tipo "immutabile" non cambino. (Se hai bisogno di modi speciali per creare istanze del tipo, puoi sempre usare ovviamente le classi di fabbrica.) Ma trovo che le interfacce per tipi immutabili semplici con semantica del valore non valgano a causa degli inconvenienti. –
possibile duplicato di [Structs, Interfaces and Boxing] (http: // stackoverflow.it/questions/3032750/structs-interfaces-and-boxing) –