I ValueTypes sono oggetti un po' particolari: sono allocati nello stack
piuttosto che nel managed-heap e le assegnazioni, se effettuate con reference
type, danno luogo a quel meccanismo che risponde al nome
di boxing/unboxing. Dicevo, regola aurea: *MAI* introdurre metodi
che cambiano lo stato interno di un value type, i risultati potrebbero essere
"strani". Esempio ( (c) Jeffrey Richter ):
public struct Point
{
int x;
int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public void ChangePoint(int x, int y) // questo è MALE
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return string.Format("({0}, {1})", x, y);
}
}
Ora
mettiam su una bella console-app:
class Program
{
static void Main(string[] args)
{
Point p = new Point(1, 1);
Console.WriteLine(p.ToString());
p.ChangePoint(2, 2);
Console.WriteLine(p.ToString());
object o = p;
Console.WriteLine(o.ToString());
((Point)o).ChangePoint(4, 4); // ATTENZIONE!!
// Qui mi aspetterei (4, 4) invece ottengo (2, 2)
Console.WriteLine(o.ToString());
Console.ReadLine();
}
}
Beccatevi l'output:
La spiegazione è molto semplice: quando
scriviamo
object o = p;
stiamo assegnando un ValueType (nello Stack) ad un ReferenceType
(nel managed heap): p, allora, viene copiato in una struttura
particolare che viene memorizzata nell'heap e il cui puntatore viene restituito
ad "o". A questo punto esso si comporta come un comunissimo tipo riferimento,
finché non effettuiamo il casting a Point (un paio di righe più in basso). Point
è un ValueType, quindi la nostra struttura deve essere di nuovo copiata
(e notate che scrivo copiata e non trasferita) nello stack tramite
l'unboxing. Su questa copia viene chiamato il metodo ChangePoint che
quindi modifica i field nella copia dello stack, e non nell'oggetto a
cui sta continuando a puntare attualmente o. Ecco perché
richiamando nuovamente la visualizzazione del punto rappresentato da o,
continuiamo a vedere (2, 2).
Magari per molti avrò fatto la scoperta dell'acqua calda perché si tratta di roba nota,
però immagino quanto tempo avrei perso a debuggare un codice del genere se non
avessi studiato il librone di Richter. E bravo Jeffrey!
powered by IMHO 1.3