Tl; dr: вы всегда можете создать дешевую оболочку IQueryable<T>
поверх IEnumerable<T>
, которая прекрасно подойдет и, вероятно, решит вашу проблему:
IEnumerable<int> nums_enumerable = new[] { 1, 2, 3 }.AsEnumerable();
IQueryable<int> nums_queryable = nums_enumerable.AsQueryable();
Длинная версия состоит в том, что ваша проблема показывает, почему интерфейсы существуют в первую очередь: сообщить компилятору, что, если два объекта реализуют IEnumerable
, то это контракт, по которому они должны реализовать все методы в этом интерфейсе и вызов этих методов не будет завершен во время выполнения.
То, что вы спрашиваете, - это полное игнорирование интерфейса, которое называется типизацией утки и фактически даже достижимо в C # с использованием отражения или DLR (ключевое слово dynamic
). Однако это действительно пахнет плохим дизайном, когда вам нужно использовать отражение, и вы внезапно теряете все проверки и гарантии во время компиляции: ваш код может работать во время выполнения, а может и нет. В этом случае это была бы очень плохая идея.
Примеры типизации утки обычно представляют собой простые варианты использования, но дело в том, что сигнатура метода все еще должна соответствовать . В данном конкретном случае это еще хуже: различные методы в этом интерфейсе имеют одно и то же имя, и кажется, работают одинаково, они совершенно разные.
Сравните эти два случая, например:
В случае IEnumerable<T>
мы звоним public static double Average<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
. Параметром этого метода является анонимный метод , т. Е. Метод в сгенерированном во время компиляции классе, который принимает параметр и возвращает значение, умноженное на 2. Метод IEnumerable.Average
фактически вызывает этот метод для каждого элемента в списке:
double GetAverage(IEnumerable<int> items)
{
return items.Average(i => i * 2);
}
В случае IQueryable<T>
мы звоним public static double Average<TSource>(this IQueryable<TSource> source, Expression<Func<TSource,int>> selector)
. Параметром этого метода является дерево выражений , то есть объект, содержащий информацию, которую мы хотим умножить на параметр 2
. Он буквально содержит двоичное выражение типа Multiply
, которое ссылается на два других выражения (a ParameterExpression
и a ConstantExpression
). Этот объект сам не выполняет никаких вычислений при передаче в IQueryable.Average
:
double GetAverage(IQueryable<int> items)
{
return items.Average(i => i * 2);
}