Crad's .NET Blog

L'UGIblog di Marco De Sanctis
posts - 190, comments - 457, trackbacks - 70

Regola aurea per chi usa Value Types

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:

Risultato sulla console

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

Print | posted on sabato 1 aprile 2006 03:23 | Filed Under [ .Net 2.0 ]

Powered by:
Powered By Subtext Powered By ASP.NET