Si ha spesso l'esigenza di interagire in maniera dinamica con i campi di una struttura.
Nessun problema per quanto riguarda la lettura con il metodo GetValue() dell'oggetto FieldInfo, ma quando si ha il bisogno di scrivere dei dati con il metodo SetValue() dello stesso oggetto, si incorre nel problema che tutto sembra andare bene ma i campi della struttura non vengono valorizzati come ci si aspetterebbe.
La spiegazione di questo comportamento è semplice: la struttura è un ValueType e risiede sullo Stack, mentre SetValue() accetta un'istanza di Object. Quindi, quello che succede invocando il metodo è che la struttura viene sottoposta a Boxing implicito e copiata nello Heap, il campo viene aggiornato in questa copia ma, in fase di Unboxing, il campo non viene riassociato di conseguenza nella struttura originale residente sullo Stack.
Per risolvere tutto questo, basta quindi sottoporre la struttura ad un Boxing esplicito _prima_ di invocare il metodo SetValue(), invocarlo passandogli la copia "Boxata" della struttura e, alla fine, sottoporre ad Unboxing la struttura modificata e riassociarla a quella originale.
In questo modo non si interpellerà il boxing implicito e non ci saranno possibilità di perdere informazioni per strada.
Di seguito un esempio di struttura
//Definizione di struttura
public struct Struttura
{
public string Campo1;
public string Campo2;
public string Campo3;
public override string ToString()
{
return "Contenuto: \n" + Campo1 + "\n" + Campo2 + "\n" + Campo3;
}
}
E del codice necessario ad aggiornarla dinamicamente. In questo caso faccio il confronto tra l'utilizzo del boxing esplicito e quelli inplicito.
//Istanzio due strutture, di cui una verrà sottoposta a boxing esplicito
Struttura strutturaDaNonBoxare = new Struttura();
Struttura strutturaDaBoxare = new Struttura();
//Prendo l'array di campi del tipo Struttura.
//E' ininfluente quale delle due strutture uso, il tipo è sempre lo stesso.
FieldInfo[] campi = strutturaDaNonBoxare.GetType().GetFields();
//Faccio il boxing di una delle strutture
object strutturaBoxed = (object)strutturaDaBoxare;
//Ciclo gli elementi dell'array e associo i valori ad entrambe le strutture utilizzando setvalue
foreach (FieldInfo campo in campi) {
campo.SetValue(strutturaBoxed,string.Format("Test {0}",Guid.NewGuid().ToString()));
campo.SetValue(strutturaDaNonBoxare,string.Format("Test {0}",Guid.NewGuid().ToString()));
}
//Aggiorno la struttura originale
strutturaDaBoxare = (Struttura)strutturaBoxed;
//Stampo l'output delle due strutture
Console.WriteLine(strutturaDaNonBoxare);
Console.WriteLine(strutturaDaBoxare);
L'output che viene visualizzato è il seguente. Da notare che la strutturaDaNonBoxare non ha i campi valorizzati.