Если я передам IQueryable как IEnumerable, тогда вызывается метод расширения Linq, реализация которого вызывается?
Учитывая следующий код:
IQueryable<T> queryable;
// something to instantiate queryable
var enumerable = (IEnumerable<T>) queryable;
var filtered = enumerable.Where(i => i > 3);
В последней строке, какой метод расширения вызывается?
Это IEnumerable<T>.Where(...)
? Или будет вызываться IQueryable<T>.Where(...)
, потому что фактическая реализация по-прежнему явно доступна для запроса?
Предположительно, идеал был бы для вызываемой версии IQueryable, так же, как обычный полиморфизм всегда будет использовать более специфическое переопределение.
В Visual Studio, хотя, когда я щелкнул правой кнопкой мыши метод Where и "Go to Definition", я попал в версию IEnumerable, что имеет смысл с визуальной точки зрения.
Моя основная проблема заключается в том, что если где-то в моем приложении я использую Linq для NHibernate, чтобы получить Queryable, но я передаю его с помощью интерфейса, который использует более общую подпись IEnumerable, я потеряю чудеса отложенного выполнения базы данных!
Edit: Оказывается, что, как указал Иридиум, это версия Enumerable, получившая название
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";
}
}
Вывод - "Метод MyString".
Ответы
Ответ 1
В настоящее время принятый ответ касается виртуальных методов, а не методов расширения.
Если вы передаете IQueryable в IEnumerable и затем вызываете один из методов расширения (например, Where (...) в своем вопросе), то будет вызываться тот, который включен в Enumerable, а не Queryable.
Ответ 2
Когда вы запускаете эту простую программу, она объясняет, как работает перегрузка:). Ответ заключается в том, что будет вызван метод базового типа. И он должен работать одинаково для IQueryable
и IEnumerable
; т.е. будет вызван метод исходного объекта, поскольку, хотя мы накладываем его на что-то другое, фактическая реализация имеет оригинальный тип. Но когда есть метод расширения, используется метод расширения для объекта, на который делается ссылка, поэтому в этом случае он зависит от логики в методе расширения. Метод расширения мог бы прийти умный логит, чтобы увидеть, является ли объект фактическим типом, а затем отбросить его до того, как он выполнит этот метод.
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";
}
}