2015-10-10 27 views
7

Zgodnie z moją wiedzą this w metodzie rozszerzenia jest przekazywana jako zmienna ref. Mogę to sprawdzić wykonującMetoda rozszerzenia i lokalna zmienna "ta"

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(); 

My List<int> ints jest teraz 1, 2, 3, 4, 5, 0.

Jednak kiedy robię

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); 

spodziewałbym mój List<int> ints być 3, 4, 5 ale pozostaje nietknięty. Czy brakuje mi czegoś oczywistego?

+0

'list' nie są przekazywane przez referencję, jest to odniesienie przekazywane przez wartość. Przekazywanie przez odniesienie powoduje aliasy zmiennej w metodzie wywołania wewnątrz osoby, która pozwala na przypisanie jej bezpośrednio. – Lee

+0

Istnieje różnica między "przekazywaniem * przez * odniesienia" i "przekazywaniem * a * odniesienia". Odniesienie do listy jest przekazywane wartością, co oznacza, że ​​możesz uzyskać dostęp do listy i ją modyfikować, ale nie dotykasz zmiennej na zewnątrz, która również odwołuje się do listy. –

+0

'(nowa lista ()). Metoda()' compiles => argument nie musi być zmienną w ogóle => nie lubić 'ref'. –

Odpowiedz

5

Parametr metody rozszerzenia metody this jest przekazywany przez wartość, a nie przez odniesienie. Oznacza to, że po wprowadzeniu metody rozszerzenia, masz dwie zmienne wskazujące na ten sam adres pamięci: oryginalny parametr ints i list. Gdy dodajesz element do listy w metodzie rozszerzenia, jest to odzwierciedlane w ints, ponieważ modyfikujesz obiekt, do którego odnoszą się obie zmienne. Po zmianie przypisania list tworzona jest nowa lista zarządzanych stert, a parametr metody rozszerzenia wskazuje tę listę. Zmienna ints nadal wskazuje na starą listę.

3

Cóż, gdy próbujesz zmodyfikować właściwość jakiejś instancji klasy, nie potrzebujesz nawet ref, ponieważ modyfikujesz instancję, a nie odwołujesz się do niej.

W tym przykładzie nie trzeba ref słowa kluczowego jak zmodyfikować właściwość:

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); 
    } 

wyjściowa: 10

I tu potrzebujemy ref słowa kluczowego, ponieważ pracują z odniesieniem, a nie przykład:

... 

    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); 
    } 

wyjściowa: 0

To samo dotyczy scenariusza, metody rozszerzeń są takie same jak w przypadku normalnych metod statycznych, a jeśli utworzysz nowy obiekt w metodzie, to albo użyjesz słowa kluczowego ref (nie jest to możliwe dla metod rozszerzenia) albo zwróć ten obiekt, w przeciwnym razie odniesienie do niego będzie Stracony.

Więc w drugim przypadku należy użyć:

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 + " "); 

wyjściowa: 3, 4, 5