2015-10-10 17 views
7

A mia conoscenza this in un metodo di estensione viene passato come variabile ref. Posso verificare questo facendoMetodo di estensione e variabile locale "this"

public static void Method<T>(this List<T> list) 
{ 
    list.Add(default(T)); 
} 

List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 }); 
ints.Method(); 

mio List<int> ints è ora 1, 2, 3, 4, 5, 0.

Tuttavia quando lo faccio

public static void Method<T>(this List<T> list, Func<T, bool> predicate) 
{ 
    list = list.Where(predicate).ToList(); 
} 

List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 }); 
ints.Method(i => i > 2); 

mi aspetto il mio List<int> ints essere 3, 4, 5 ma rimane intatta. Mi manca qualcosa di ovvio?

+0

'list' non è passato per riferimento, è un riferimento passato per valore. Passando tramite alias di riferimento la variabile nel metodo di chiamata all'interno del callee che consente di assegnarla direttamente. – Lee

+0

C'è una differenza tra "passare * per * riferimento" e "passare * un * riferimento". Il riferimento alla lista viene passato per valore, il che significa che è possibile accedere e modificare l'elenco, ma non si sta toccando la variabile all'esterno che fa anche riferimento alla lista. –

+0

'(nuovo Elenco ()). Il metodo()' compiles => argomento non deve essere necessariamente una variabile => non come 'ref'. –

risposta

5

Il parametro del metodo di estensione this viene passato per valore, non per riferimento. Ciò significa che, entrando nel metodo di estensione, hai due variabili che puntano allo stesso indirizzo di memoria: l'originale ints e il parametro list. Quando si aggiunge un elemento all'elenco all'interno del metodo di estensione, questo viene riflesso in ints, poiché si modifica un oggetto a cui fanno riferimento entrambe le variabili. Quando si riassegna lo list, viene creato un nuovo elenco sull'heap gestito e il parametro del metodo di estensione punta a questo elenco. La variabile ints punta ancora alla vecchia lista.

3

Bene, quando si tenta di modificare la proprietà di alcune istanze di classe non è nemmeno necessario ref poiché si modifica l'istanza piuttosto che si fa riferimento ad essa.

In questo esempio non è necessario ref parola chiave come si modifica proprietà:

class MyClass 
    {    
     public int MyProperty { get; set; } 
    } 

    static void Method(MyClass instance) 
    { 
     instance.MyProperty = 10;      
    } 

    static void Main(string[] args) 
    { 
     MyClass instance = new MyClass(); 
     Method(instance); 

     Console.WriteLine(instance.MyProperty); 
    } 

di uscita: 10

E qui si ha bisogno ref parola chiave perché si lavora con riferimento e non con esempio:

... 

    static void Method(MyClass instance) 
    { 
     // instance variable holds reference to same object but it is different variable 
     instance = new MyClass() { MyProperty = 10 }; 
    } 

    static void Main(string[] args) 
    { 
     MyClass instance = new MyClass(); 
     Method(instance); 

     Console.WriteLine(instance.MyProperty); 
    } 

uscita: 0

È lo stesso per il tuo scenario, i metodi di estensione sono gli stessi dei normali metodi statici e se crei un nuovo oggetto all'interno del metodo allora usi la parola chiave ref (non è però possibile per i metodi di estensione) o restituisci questo oggetto altrimenti il ​​riferimento ad esso sarà perduto.

Quindi nel tuo secondo caso si dovrebbe usare:

public static List<T> Method<T>(this List<T> list, Func<T, bool> predicate) 
{ 
    return list.Where(predicate).ToList(); 
} 

List<int> ints = new List<int>(new int[] { 1, 2, 3, 4, 5 }); 
ints = ints.Method(i => i > 2); 

foreach(int item in ints) Console.Write(item + " "); 

uscita: 3, 4, 5

Problemi correlati