2011-01-07 5 views
14

Biorąc pod uwagę następujący kod:Jeśli mogę rzucić IQueryable jako IEnumerable, a następnie wywołać metodę rozszerzenia Linq, która implementacja zostanie wywołana?

IQueryable<T> queryable; 

// something to instantiate queryable 

var enumerable = (IEnumerable<T>) queryable; 

var filtered = enumerable.Where(i => i > 3); 

W końcowej linii, która jest wywoływana metoda rozszerzenie?

Czy to jest IEnumerable<T>.Where(...)? A może zadzwonić pod numer IQueryable<T>.Where(...), ponieważ faktyczna implementacja nadal jest oczywiście możliwa do sprawdzenia?

Prawdopodobnie idealnym rozwiązaniem byłoby wywołanie wersji IQueryable w taki sam sposób, w jaki normalny polimorfizm zawsze będzie korzystał z bardziej szczegółowego nadpisania.

Jednak w Visual Studio, gdy klikam prawym przyciskiem myszy metodę Where i "Go to Definition", jestem przenoszony do wersji IEnumerable, która ma sens z wizualnego punktu widzenia.

Moim głównym zmartwieniem jest to, że jeśli gdzieś w mojej aplikacji używam Linq do NHibernate, aby uzyskać Queryable, ale przekazuję to za pomocą interfejsu, który używa bardziej ogólnego podpisu IEnumerable, stracę cuda odroczonego wykonania bazy danych !


Edit: Okazuje się, że jak Iridium podkreślił, że jest to wersja Enumerable że jest wywoływana

public class Program 
    { 
     static void Main(string[] args) 
     { 
      var myString2 = new MyString2(); 
      var myString = (MyString)myString2; 
      Console.WriteLine(myString.Method()); 
      Console.ReadLine(); 
     } 
    } 

    public class MyString {} 

    public class MyString2 : MyString {} 

    public static class ExtensionMethods 
    { 
     public static string Method(this MyString instance) 
     { 
      return "MyString method"; 
     } 

     public static string Method(this MyString2 instance) 
     { 
      return "MyString2 method"; 
     } 
    } 

Wyjście jest "metoda MyString".

+0

Zapomniałem zagłosować na pytanie ... było ciekawie się dowiedzieć :). –

+0

http://stackoverflow.com/questions/474074/overriding-extension-methods –

+3

Pamiętaj, że składnia metody rozszerzenia jest po prostu syntaktycznym cukrem dla * wywoływania statycznej metody * i statycznych metod z definicji * użyj analizy statycznej, aby określić, która metoda połączenie*. Jeśli potrzebujesz * analizy środowiska wykonawczego *, musisz napisać kod samodzielnie, aby to zrobić. Ponadto: * nie * jest uważane za pożądane dla IQ, który statycznie IE niespodziewanie zmienia się w ścieżkę kodu IQ; przypuszczalnie rzucisz go na IE, ponieważ * nie chcesz * traktować go jako IQ! Jeśli chcesz traktować to jako iloraz inteligencji, to nie odrzucaj go *. Zasada Groucho: jeśli boli, kiedy to robisz, * nie rób tego *. –

Odpowiedz

8

Aktualnie zaakceptowana odpowiedź dotyczy metod wirtualnych, a nie metod rozszerzania.

Jeśli rzutujesz IQueryable na IEnumerable, a następnie wywołujesz jedną z metod rozszerzenia (np. Where (...) w twoim pytaniu), to wywoła się wersję jednego z wersji Enumerable, a nie Queryable.

3

Po uruchomieniu tego prostego programu wyjaśnia, jak działa przeciążenie :). Odpowiedź brzmi, że zostanie wywołana metoda bazowego typu. I powinno działać tak samo dla IQueryable i IEnumerable; oznacza to, że metoda obiektu oryginalnego zostanie wywołana, ponieważ mimo tego, że rzutujemy ją na coś innego, rzeczywista implementacja jest oryginalna. Ale gdy istnieje metoda rozszerzenia, używana jest metoda rozszerzenia dla obiektu, do którego się odwołuje, więc w takim przypadku zależy to od logiki w metodzie rozszerzenia. Metoda rozszerzenia mogłaby pochodzić sprytnie, aby sprawdzić, czy obiekt jest rzeczywistego typu, a następnie odrzucić go przed wykonaniem metody.

class Program 
{ 
    static void Main(string[] args) 
    { 
     MyString2 myString2 = new MyString2(); 
     var myString = (MyString)myString2; 
     Console.WriteLine(myString); // prints "ToString of MyString2" 
     Console.WriteLine(myString.GiveMeTheString()); // prints "GiveMeTheString of MyString2" 
     Console.ReadLine(); 
    } 
} 

public class MyString 
{ 
    public string MyProperty { get; set; } 
    public override string ToString() 
    { 
     return "ToString of MyString"; 
    } 
} 
public class MyString2 : MyString 
{ 
    public string MyProperty { get; set; } 
    public override string ToString() 
    { 
     return "ToString of MyString2"; 
    } 
} 

public static class Extensions 
{ 
    public static string GiveMeTheString(this MyString myString) 
    { 
     return "GiveMeTheString of MyString"; 
    } 
    public static string GiveMeTheString(this MyString2 myString) 
    { 
     return "GiveMeTheString of MyString2"; 
    } 
} 
+0

Dziękuję Tomas! Wiedziałem, że powinienem być w stanie to zrobić przez kodowanie, ale mój mózg nie działa! Przynajmniej teraz, jeśli ktoś szukający rozwiązania z nadzieją napotka ten post. –

+0

Powinienem dodać - odpowiedź brzmi, że zostanie wywołana bardziej szczegółowa (IQueryable) metoda rozszerzenia. –

+0

Dzieje się z najlepszymi z nas. Pamiętaj, aby głosować i akceptować odpowiedź, aby inni mogli łatwiej znaleźć odpowiedź; to znaczy, jeśli jest poprawna. I dodam tę część gdzieś w mojej odpowiedzi. –