Domanda

  

Ci sono casi in cui un'istanza di a   il tipo di valore deve essere trattato come un   istanza di un tipo di riferimento. Per   situazioni come questa, un tipo di valore   l'istanza può essere convertita in a   istanza del tipo di riferimento tramite a   processo chiamato boxe. Quando un valore   tipo istanza è inscatolata, lo spazio di archiviazione   allocato sull'heap e il   il valore dell'istanza viene copiato in quello   spazio. Un riferimento a questa memoria è   messo in pila. Il valore inscatolato   è un oggetto, un tipo di riferimento che   contiene il contenuto del valore   digitare istanza.

     

Comprensione del Common Type System di .NET

In Wikipedia c'è un esempio per Java. Ma in C #, quali sono alcuni casi in cui si dovrebbe inscatolare un tipo di valore? O sarebbe una domanda migliore / simile, perché si dovrebbe archiviare un tipo di valore nell'heap (in scatola) anziché nello stack?

È stato utile?

Soluzione

In generale, in genere si desidera evitare l'inscatolamento dei tipi di valore.

Tuttavia, ci sono rare occasioni in cui ciò è utile. Se è necessario targetizzare il framework 1.1, ad esempio, non si avrà accesso alle raccolte generiche. Qualsiasi utilizzo delle raccolte in .NET 1.1 richiederebbe il trattamento del tipo di valore come System.Object, che provoca boxe / unboxing.

Ci sono ancora casi in cui ciò può essere utile in .NET 2.0+. Ogni volta che si desidera sfruttare il fatto che tutti i tipi, compresi i tipi di valore, possono essere trattati direttamente come un oggetto, potrebbe essere necessario utilizzare il boxing / unboxing. Questo può essere utile a volte, poiché ti consente di salvare qualsiasi tipo in una raccolta (utilizzando l'oggetto anziché T in una raccolta generica), ma in generale è meglio evitarlo, poiché stai perdendo la sicurezza del tipo. L'unico caso in cui si verifica frequentemente la boxe è quando si utilizza Reflection: molte delle chiamate in reflection richiedono la boxe / unboxing quando si lavora con tipi di valore, poiché il tipo non è noto in anticipo.

Altri suggerimenti

Non c'è quasi mai un buon motivo per inscatolare deliberatamente un tipo di valore. Quasi sempre, il motivo per inscatolare un tipo di valore è archiviarlo in una raccolta che non è a conoscenza del tipo. La vecchia ArrayList , ad esempio, è una raccolta di oggetti, che sono tipi di riferimento. L'unico modo per raccogliere, diciamo, numeri interi, è inscatolarli come oggetti e passarli a ArrayList.

Oggi abbiamo raccolte generiche, quindi questo è meno un problema.

La boxe generalmente avviene automaticamente in .NET quando è necessario; spesso quando si passa un tipo di valore a qualcosa che prevede un tipo di riferimento. Un esempio comune è string.Format (). Quando si passano i tipi di valore di base a questo metodo, vengono inseriti in un riquadro come parte della chiamata. Quindi:

int x = 10;
string s = string.Format( "The value of x is {0}", x ); // x is boxed here

Questo illustra un semplice scenario in cui un tipo di valore (x) viene automaticamente inscatolato per essere passato a un metodo che prevede un oggetto. In generale, si desidera evitare tipi di valore di boxe quando possibile ... ma in alcuni casi è molto utile.

A parte l'interesse, quando si usano generici in .NET, i tipi di valore non vengono inscatolati se usati come parametri o membri del tipo. Il che rende i generici più efficienti del vecchio codice C # (come ArrayList) che trattano tutto come {oggetto} come tipo agnostico. Ciò aggiunge un motivo in più per utilizzare raccolte generiche, come List<T> o Dictionary<T,K> su ArrayList o Hashtable.

Ti consiglierei 2 bei articoli di Eric Lippert

http : //blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx

http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx

Ecco la citazione con cui sarei d'accordo al 100%

  

Utilizzo dello stack per i locali di valore   type è solo un'ottimizzazione che il   CLR si esibisce per tuo conto.   La caratteristica rilevante dei tipi di valore è   che hanno la semantica dell'essere   copiato per valore, non che a volte   la loro deallocazione può essere ottimizzata da   l'autonomia.

Nel 99% delle applicazioni gli sviluppatori non dovrebbero preoccuparsi del motivo per cui i tipi di valore sono nello stack e non nell'heap e quale aumento delle prestazioni possiamo avere qui. Le juts hanno in mente regole molto semplici:

  1. Evita boxe / unboxing quando no necessario, utilizzare raccolte generiche. La maggior parte dei problemi si verifica non quando tu definire i tuoi tipi, ma quando tu usa i tipi esistenti non correttamente (definito da Microsoft o dal tuo colleghi)
  2. Crea i tuoi tipi di valore semplice. Se hai bisogno di avere una struttura con 10-20 campi, suppongo che tu vada meglio creare una classe. Immagina tutto quei campi verranno copiati ogni volta quando lo passi di tanto in tanto a funzione per valore ...
  3. Non credo sia molto utile tipi di valore con tipo di riferimento campi all'interno. Come strutt con Campi stringa e oggetto.
  4. Definisci il tipo che ti serve a seconda funzionalità richiesta, non su dove dovrebbe essere memorizzato. Le strutture hanno funzionalità limitata rispetto a classi, quindi se struct non può fornire la funzionalità richiesta, come costruttore predefinito, definire la classe.
  5. Se qualcosa può eseguire qualsiasi azioni con i dati di altri tipi, di solito è definito come a classe. Per operazioni su strutture con tipi diversi dovrebbero essere definiti solo se riesci a trasmettere un tipo a un altro. Supponiamo che tu possa aggiungere int a doppio perché puoi lanciare int doppia.
  6. Se qualcosa dovrebbe essere apolide, è una classe.
  7. Quando esiti, usa i tipi di riferimento. : -)

Qualsiasi regola consente l'esclusione in casi speciali, ma non tentare di ottimizzare eccessivamente.

P.S. Ho incontrato alcuni sviluppatori ASP.NET con 2-3 anni di esperienza che non conoscono la differenza tra stack e heap. :-( Non assumerei una persona simile se fossi un intervistatore, ma non perché la boxe / unboxing potrebbe essere un collo di bottiglia in nessuno dei siti ASP.NET che abbia mai visto.

Penso che un buon esempio di boxe in c # si presenti nelle raccolte non generiche come ArrayList .

Un esempio è quando un metodo accetta un parametro oggetto e deve essere passato un tipo di valore.

Di seguito alcuni esempi di boxe / unboxing

ArrayList ints = new ArrayList();
myInts.Add(1); // boxing
myInts.Add(2); // boxing

int myInt = (int)ints [0]; // unboxing

Console.Write("Value is {0}", myInt); // boxing

Una delle situazioni in cui ciò accade è ad esempio se si dispone di un metodo che prevede parametri di tipo oggetto e si sta passando uno dei tipi primitivi, ad esempio int. Oppure se si definisce il parametro come 'ref' di tipo int.

Il codice

int x = 42;
Console.Writeline("The value of x is {0}", x );

effettivamente box e unboxes perché Writeline esegue un int cast all'interno. Per evitare ciò, potresti farlo

int x = 42;
Console.Writeline("The value of x is {0}", x.ToString());

Attenzione ai bug sottili!

Puoi dichiarare i tuoi tipi di valore dichiarando il tuo tipo come struct. Immagina di dichiarare un ArrayList con molte proprietà e di inserire alcune istanze in un []. Questo li inscatola ovviamente. Ora fai riferimento a uno tramite l'operatore readonly, esegui il cast sul tipo e imposta una proprietà. Hai appena impostato una proprietà su una copia . Quello in <=> è ancora non modificato.

Per questo motivo, i tipi di valore devono essere sempre immutabili, ovvero rendere tutte le variabili membro <=> in modo che possano essere impostate solo nel costruttore e non abbiano alcun tipo mutabile come membri.

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