Domanda

Se sto passando un oggetto a un metodo, perché dovrei usare la parola chiave ref? Non è comunque questo il comportamento predefinito?

Ad esempio:

class Program
{
    static void Main(string[] args)
    {
        TestRef t = new TestRef();
        t.Something = "Foo";

        DoSomething(t);
        Console.WriteLine(t.Something);
    }

    static public void DoSomething(TestRef t)
    {
        t.Something = "Bar";
    }
}


public class TestRef
{
    public string Something { get; set; }
}

L'output è " Bar " il che significa che l'oggetto è stato passato come riferimento.

È stato utile?

Soluzione

Passa a ref se vuoi cambiare l'oggetto:

TestRef t = new TestRef();
t.Something = "Foo";
DoSomething(ref t);

void DoSomething(ref TestRef t)
{
  t = new TestRef();
  t.Something = "Not just a changed t, but a completely different TestRef object";
}

Dopo aver chiamato DoSomething, t non si riferisce all'originale new TestRef, ma si riferisce a un oggetto completamente diverso.

Anche questo può essere utile se vuoi cambiare il valore di un oggetto immutabile, ad es. a string. Non è possibile modificare il valore di int dopo che è stato creato. Ma usando un <=>, potresti creare una funzione che cambia la stringa con un'altra con un valore diverso.

Modifica: come altri hanno già detto. Non è una buona idea usare <=> a meno che non sia necessario. L'uso di <=> dà al metodo la libertà di modificare l'argomento per qualcos'altro, i chiamanti del metodo dovranno essere codificati per assicurarsi di gestire questa possibilità.

Inoltre, quando il tipo di parametro è un oggetto, le variabili oggetto fungono sempre da riferimenti all'oggetto. Ciò significa che quando viene utilizzata la parola chiave <=> hai un riferimento a un riferimento. Ciò ti consente di fare le cose come descritto nell'esempio sopra riportato. Tuttavia, quando il tipo di parametro è un valore di base (ad es. <=>), quindi se questo parametro è assegnato all'interno del metodo, il valore dell'argomento passato verrà modificato dopo la restituzione del metodo:

int x = 1;
Change(ref x);
Debug.Assert(x == 5);
WillNotChange(x);
Debug.Assert(x == 5); // Note: x doesn't become 10

void Change(ref int x)
{
  x = 5;
}

void WillNotChange(int x)
{
  x = 10;
}

Altri suggerimenti

Devi distinguere tra " passando un riferimento per valore " ;, e " passando un parametro / argomento per riferimento " ;.

Ho scritto un un articolo ragionevolmente lungo sull'argomento per evitare di dover scrivere attentamente ogni volta che questo accade sui newsgroup :)

In .NET quando si passa qualsiasi parametro a un metodo, viene creata una copia. Nei tipi di valore significa che qualsiasi modifica apportata al valore rientra nell'ambito del metodo e va persa quando si esce dal metodo.

Quando si passa un Tipo di riferimento, viene anche creata una copia, ma è una copia di un riferimento, ovvero ora si hanno DUE riferimenti in memoria per lo stesso oggetto. Pertanto, se si utilizza il riferimento per modificare l'oggetto, viene modificato. Ma se modifichi il riferimento stesso - dobbiamo ricordare che è una copia - anche eventuali modifiche andranno perse all'uscita dal metodo.

Come si è detto prima, un compito è una modifica del riferimento, quindi va perso:

public void Method1(object obj) {   
 obj = new Object(); 
}

public void Method2(object obj) {  
 obj = _privateObject; 
}

I metodi sopra non modificano l'oggetto originale.

Una piccola modifica del tuo esempio

 using System;

    class Program
        {
            static void Main(string[] args)
            {
                TestRef t = new TestRef();
                t.Something = "Foo";

                DoSomething(t);
                Console.WriteLine(t.Something);

            }

            static public void DoSomething(TestRef t)
            {
                t = new TestRef();
                t.Something = "Bar";
            }
        }



    public class TestRef
    {
    private string s;
        public string Something 
        { 
            get {return s;} 
            set { s = value; }
        }
    }

Poiché TestRef è una classe (che sono oggetti di riferimento), è possibile modificare il contenuto all'interno di t senza passarlo come riferimento. Tuttavia, se si passa t come riferimento, TestRef può modificare ciò a cui si riferisce la t originale. vale a dire farlo puntare a un oggetto diverso.

Con ref puoi scrivere:

static public void DoSomething(ref TestRef t)
{
    t = new TestRef();
}

E t verrà modificato dopo il completamento del metodo.

Pensa alle variabili (ad esempio foo) dei tipi di riferimento (ad esempio List<T>) come a contenere identificatori di oggetto del modulo " Oggetto # 24601 " ;. Supponiamo che l'istruzione foo = new List<int> {1,5,7,9}; induca foo.Length a contenere & Quot; Oggetto # 24601 & Quot; (un elenco con quattro elementi). Quindi chiamare ref chiederà l'oggetto # 24601 per la sua lunghezza e risponderà 4, quindi <=> sarà uguale a 4.

Se <=> viene passato a un metodo senza utilizzare <=>, tale metodo potrebbe apportare modifiche all'oggetto # 24601. Come conseguenza di tali modifiche, <=> potrebbe non essere più uguale a 4. Il metodo stesso, tuttavia, non sarà in grado di cambiare <=>, che continuerà a contenere & Quot; Oggetto # 24601 & Quot ;.

Il passaggio di <=> come parametro <=> consentirà al metodo chiamato di apportare modifiche non solo all'oggetto # 24601, ma anche a <=> stesso. Il metodo potrebbe creare un nuovo oggetto # 8675309 e memorizzare un riferimento a quello in <=>. In tal caso, <=> non conterrebbe più & Quot; Oggetto # 24601 & Quot ;, ma invece & Quot; Oggetto # 8675309 & Quot ;.

In pratica, le variabili del tipo di riferimento non contengono stringhe della forma " Oggetto # 8675309 " ;; non contengono nemmeno nulla che possa essere significativamente convertito in un numero. Anche se ogni variabile del tipo di riferimento conterrà alcuni pattern di bit, non esiste una relazione fissa tra i pattern di bit memorizzati in tali variabili e gli oggetti che identificano. Non è possibile che il codice estragga informazioni da un oggetto o un riferimento ad esso, e in seguito determini se un altro riferimento ha identificato lo stesso oggetto, a meno che il codice non contenesse o conoscesse un riferimento che identificava l'oggetto originale.

È come passare un puntatore a un puntatore in C. In .NET questo ti permetterà di cambiare ciò a cui fa riferimento la T originale, personalmente anche se penso che se lo stai facendo in .NET probabilmente hai un problema di progettazione!

Usando la parola chiave ref con i tipi di riferimento, si passa effettivamente un riferimento al riferimento. In molti modi è lo stesso che usare la parola chiave out ma con la minima differenza che non esiste alcuna garanzia che il metodo assegnerà effettivamente qualsiasi cosa al parametro <=> ed.

ref imita (o si comporta) come un'area globale solo per due ambiti:

  • Caller
  • Callee.

Se stai passando un valore, tuttavia, le cose sono diverse. È possibile forzare il passaggio di un valore per riferimento. Ciò ti consente di passare un numero intero a un metodo, ad esempio, e di fare in modo che il metodo modifichi il numero intero per tuo conto.

Ref indica se la funzione può mettere le mani sull'oggetto stesso o solo sul suo valore.

Il passaggio per riferimento non è associato a una lingua; è una strategia di associazione dei parametri accanto al valore per passaggio, passaggio per nome, passaggio per necessità, ecc ...

Un sidenote: il nome della classe TestRef è una scelta orribilmente negativa in questo contesto;).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top