PDA

View Full Version : C# Se son classi referenzieranno...


gamon02
06-02-2007, 20:04
Oggi ho scoperto un'altro poco della mia ignoranza.
Vado a spiegare cosa mi opprime.

Io ho una classe banale:

public class Hello
{
private int x = 0;

public Hello()
{ }

public int Ics
{
get { return this.x; }
set { this.x = value; }
}
}

Perfetto, ora mettiamo, da un form Windows apro un'altro form dandogli in pasto una nuova istanza della mia classe:


Hello m_hello = new Hello();
m_hello.Ics = 5;

Formetto m_frm = new Formetto();
// Si assuma che .Dati sia una proprietà object del form
m_frm.Dati = m_hello;
m_frm.ShowDialog(this);

Perfetto, ora dall'altra parte, ovvero nel form Formeto (sono un genio dei nomi) farò un casting di Dati, modificherò la proprietà Ics e chiuderò il form, SENZA riassegnare a .Dati la classe (occhio).

// On Load
Hello m_frmhello = (Hello)this.Dati;
m_frmhello.Ics = 10;
this.Close();

Al ritorno nel nostro form di origine, l'istanza della classe Hello, m_hello, avrà il nuovo valore di Ics uguale a 10.

Questo comportamento fa presuppore un passaggio per ref o out, quando io invece non ho mai chiesto una cosa del genere!!

Invece passando a .Dati un oggetto di tipo non classe, ovvero int, string.. etc.. questo passaggio per referenza non avviene, ovvero il mio valore resta bello pacioso sul 5.

Ora, perchè accade questo? E c'è un sistema per evitarlo, oltre che fare un deep Clone della classe prima di passarglila a .Dati?

Grazie un miliardo o forse più

noregret
07-02-2007, 08:05
Ciao, di default (cioè senza specificare ref o out) il tipo di dato passato a funzione in c# è di tipo ByVal (per chi conosce VB) : viene cioè eseguita una copia in memoria della variabile passata.
Per i value type ciò non ti permetterà di mutare il loro valore all'interno della funzione. Per i tipi oggetto invece qualsiasi assegnazione ai membri interni della classe avrà effetto ma non ti sarà permesso modificare il riferimento stesso alla variabile (es. settando a null l'oggetto).
Scusa se non sono stato molto chiaro... è un concetto non molto semplice da spiegare a parole :-)

Qui (http://tinyurl.com/r98gr) lo spiegano sicuramente meglio! Ciao!

andbin
07-02-2007, 16:14
Ora, perchè accade questo?È la stessa identica cosa anche in Java, ad esempio.

Quando assegni una variabile ad un'altra variabile, ne copi il contenuto ma nel caso di variabili di tipo "reference", quello che copi è il reference (il riferimento all'oggetto), non l'oggetto stesso.

Quindi nel tuo caso, m_hello e m_frm.Dati fanno riferimento alla stessa identica istanza della classe Hello.

gamon02
07-02-2007, 19:22
Grazie ad entrambi, sì avevo dimenticato di leggere il capitolo su i tipi. ;)

E' una cosa abbastanza noiosa, in quanto per le classi di mamma Microsoft il metodo Clone è quasi sempre disponibile, mentre a me tocca implementarlo e per classi belle toste è una bella scocciatura.
Sarebbe stato bello anche per il C# implementare una sorta di ByVal per passare una copia dell'oggetto. Affidandosi alla copia pari pari di memoria magari, senza sapere ne leggere ne scrivere.

noregret
08-02-2007, 11:01
Grazie ad entrambi, sì avevo dimenticato di leggere il capitolo su i tipi. ;)

Di nulla, figurati... siam qua tutti per imparare

Sarebbe stato bello anche per il C# implementare una sorta di ByVal per passare una copia dell'oggetto. Affidandosi alla copia pari pari di memoria magari, senza sapere ne leggere ne scrivere.

Ti avverto : anche il ByVal funziona esattamente allo stesso modo per i reference type ! L'unico modo per non modificare internamente la tua classe originale è passare a funzione il clone del tuo oggetto.
Se le tue classi non necessitano di ereditare da altri oggetti crea un classe base che esegua (tramite la reflection) il deep clone del tuo oggetto. Ho cercato velocemente e ho trovato un esempio qui :

Deep Clone (http://www.csharpfr.com/code.aspx?ID=34850)

In questo modo non dovrai implementare manualmente su ogni tuo oggetto un custom clone (che comportebbe sicuramente a problemi di gestione futura in quanto tutte le volte che aggiungi membri interni di tipo reference devi ricordarti di implementarli nella clone).

gamon02
08-02-2007, 11:54
Ma sei un.. un... un grande ;)
Grazie un miliardo o forse due