2010-02-03 17 views
9

Ho letto la differenza tra i parametri passng e non passing ref in, tuttavia, quando dovrei volerli utilizzare?Quando passare la parola chiave ref in

Ad esempio, avevo un po 'di logica in un metodo che poteva essere refactored nel proprio metodo. Resharper 4.5 ha reso uno dei parametri un ref type ma non pensavo di averlo fatto se avessi eseguito il refactoring manualmente.

Ovviamente mi manca un po 'di comprensione. Forse un esempio di cosa succede quando certi tipi o determinati scenari nella codifica mancano della parola chiave ref aiuterà?

Grazie

+1

e quando passare 'out'? – Natrium

+0

codice postale per curiosità? – bdd

+0

@Natrium: Trovo che il tempo sia dopo aver rigettato alcuni a molti :) –

risposta

8

Permettetemi di rompere che verso il basso in due domande:

1) Quando si dovrebbe usare dichiarazioni di parametri ref/out formali quando scrivere un metodo?

Usa Rif/fuori quando si desidera il metodo di essere in grado di leggere e scrivere una variabile passato in dal chiamante, piuttosto che limitarsi a leggere un valore di.

2) Perché un refactoring "metodo di estrazione" produce un parametro ref?

Non conosco i dettagli di Resharper, ma posso indovinare.Si consideri il seguente male mutabile tipo di valore:

struct S 
{ 
    private int x; 
    public int X() { return this.x; } 
    public void M() { this.x += 1; } 
} 

Hai un metodo:

void Foo() 
{ 
    S s = new S(); 
    Fred(s); 
    Blah(s); 
    Bar(s); 
    s.M(); 
    Console.WriteLine(s.X()); // prints 1 
} 

e lo fai "metodo estratto" nella mano di mezzo:

void NewMethod(ref S s) 
{ 
    Blah(s); 
    Bar(s); 
    s.M(); 
} 

void Foo() 
{ 
    S s = new S(); 
    Fred(s); 
    NewMethod(ref s); 
    Console.WriteLine(s.X()); // still prints 1 
} 

Se invece hai fatto un metodo senza "ref", quindi chiamando NewMethod (s) passerebbe una copia di s a NewMethod. Ricorda, i tipi di valore sono copiati per valore; è per questo che li abbiamo chiamati "tipi di valore". Sarebbe la copia che viene mutata, e quindi s.X() restituisce zero. È una cattiva idea per un refactoring introdurre una modifica semantica in un programma, ed è difficile per un motore di refactoring sapere se un determinato metodo si basa sulla mutabilità di un tipo di valore o meno.

Questo è solo un altro motivo per cui è necessario evitare tipi di valori mutabili.

1

Passando byref ha senso solo per "effetti collaterali" di una funzione: vale a dire, che si intende modificare un parametro di valore-tipo, o riassegnare un altro oggetto per un dato parametro oggetto, e che hanno il cambiamento sopravvivere la chiamata di funzione. Esempio: TryGetValue().

Altrimenti, meglio attenersi a byval.

+1

In realtà 'TryGetValue()' utilizza la parola chiave 'out' in cui il chiamante non è richiesto per inizializzare la variabile, ma il chiamato è. La parola chiave 'ref' viene utilizzata nei casi in cui il callee * MAGGIO * modifica il riferimento dell'oggetto. –

0

Concettualmente, la differenza è che un tipo di valore memorizza direttamente il suo valore, mentre un tipo di riferimento memorizza un riferimento al valore. Forse dovresti rileggere un po 'sui tipi di riferimento vs valore.

Passare i tipi di valore per riferimento - come dimostrato sopra - è utile, ma ref è anche utile per il passaggio dei tipi di riferimento. Ciò consente ai metodi chiamati di modificare l'oggetto a cui fa riferimento il riferimento poiché il riferimento stesso viene passato per riferimento. Il seguente esempio mostra che quando un tipo di riferimento viene passato come parametro ref, l'oggetto stesso può essere modificato.

class RefRefExample 
{ 
    static void Method(ref string s) 
    { 
     s = "changed"; 
    } 
    static void Main() 
    { 
     string str = "original"; 
     Method(ref str); 
     // str is now "changed" 
    } 
} 

MSDN

0

L'effetto è che eventuali modifiche al parametro nel metodo si rifletteranno in quella variabile quando il controllo ripassa al metodo chiamante. Quindi, se si desidera che il valore assegnato al parametro di sopravvivere oltre la chiamata di metodo è un possibile caso d'uso

0

Considerate questo esempio:

static int i = 3; 

public static void ChangeIntRef(ref int val) 
{ 
    val = 5; 
} 

public static void ChangeInt(int val) 
{ 
    val = 5; 
} 

Console.WriteLine(i); 
ChangeInt(i); 
Console.WriteLine(i); 

ChangeIntRef(ref i); 
Console.WriteLine(i); 

Passando il parametro come ref, si indica al compilatore che quello che realmente vuole è un riferimento alla variabile originale per essere passato al metodo. Di conseguenza, il metodo può modificare il valore della variabile originale.

Se si esegue il frammento sopra, il risultato è:

3 
3 
5 

Questo dovrebbe mostrare chiaramente che senza la parola chiave ref, il metodo ChangeInt non è in grado di cambiare realmente il valore originale. Tuttavia, con la parola chiave ref il metodo ChangeIntRef è in grado di modificare il valore originale.

0

Uso i riferimenti per la semantica. Considerate questo approccio:

void AddResultsTable(ref PlaceHolder p) // modifies p; adding a table 
{ 
    var t = new Table(); 

    AddTableHeader(ref t); // modifies t; adding a table header 

    AddTableBody(ref t); // modifies t; adding a table body 

    AddTableFooter(ref t); // modifies t; adding a table footer 

    p.Controls.Add(t); 
} 

AddResultsTable(ref PlaceHolderResults); 

Versus questo:

Table ReturnTable() 
{ 
    var t new Table(); 

    // AddTableHeader() returns TableHeader 
    t.Columns.HeaderColumns.Add(ReturnTableHeader()); 

    // ... etc. 

    return t; 
} 

PlaceHolder.Controls.Add(ReturnTable()); 

Il primo frammento di codice sembra più pulito per me; i metodi modificano gli oggetti piuttosto che restituire nuovi oggetti che a loro volta devono essere aggiunti. Tutto rimane "inscatolato" e nascosto, dentro i metodi.