为什么我不能用扩展方法中的通用类型变量替换 IEnumerable<T>?

Why can't I replace IEnumerable<T> by a generic type variable in extension method?

我正在尝试使扩展方法更通用以避免冗余(Here is an example 一些实际代码,下面的代码只是为了演示这个问题 - 我有想法使该方法可用于 IQueryable<T>)。

以下工作正常:

public static class Extensions
{
    public static IEnumerable<T> MySelect1<T, V>(this IEnumerable<T> query, Func<T, V> f)
    {
        // do something, then return IEnumerable<T>
        var result=query.AsEnumerable<T>();
        return result;
    }
    public static IQueryable<T> MySelect1<T, V>(this IQueryable<T> query, Func<T, V> f)
    {
        // do something, then return IQueryable<T>
        var result = query.AsQueryable<T>();
        return result;
    }
}

我可以像这样在 LinqPad 中使用它(当与 Northwind 数据库连接时):

var myQuery=(from x in Customers select x);
myQuery.AsEnumerable().MySelect1(d => d.CustomerID).Dump();
myQuery.AsQueryable().MySelect1(d => d.CustomerID).Dump();

现在我想摆脱 MySelect1 的重复实现,所以我将其重构为:

public static class Extensions
{
    public static E MySelect2<E, T, V>(this E query, Func<T, V> f)
    where E : System.Linq.IQueryable<T>, System.Collections.Generic.IEnumerable<T>
    {
        return (E)query.Select(f);
    }
}

这也能编译,但我不能像上面那样使用 MySelect2,请考虑以下几点:

// CS0411 The type arguments for method 'Extensions.MySelect2<E, T, V>(E, Func<T, V>)' 
// cannot be inferred from the usage. Try specifying the type arguments explicitly.
myQuery.AsEnumerable().MySelect2(d => d.CustomerID).Dump(); 
myQuery.AsQueryable().MySelect2(d => d.CustomerID).Dump();

好的,执行错误要求的操作适用于此代码行:

myQuery.AsQueryable()
       .MySelect2<IQueryable<Customers>, Customers, String>(d => d.CustomerID).Dump();

但不是那个:

myQuery.AsEnumerable<Customers>()
       .MySelect2<IEnumerable<Customers>, Customers, String>(d => d.CustomerID).Dump();

在这里,我得到

CS0311 The type 'System.Collections.Generic.IEnumerable' cannot be used as type parameter 'E' in the generic type or method 'Extensions.MySelect2(E, Func)'. There is no implicit reference conversion from 'System.Collections.Generic.IEnumerable' to 'System.Linq.IQueryable'.

为什么?如何解决?请帮忙

Why?

正是由于错误消息中所述的原因:您尝试使用 IEnumerable<Customers> 作为 E 的类型参数,但 E 具有此约束:

where E : System.Linq.IQueryable<T>

And how can it be fixed?

它不能,假设我理解你想要实现的目标。

您要实现的 "simplification" 存在一个根本问题:您实际上并没有完全复制原始的 MySelect1 方法。第一个调用 AsEnumerable(),第二个调用 AsQueryable()。您正在尝试用强制转换替换它们,但那是行不通的。

还有一个问题,即使是你原来的方法:你接受 Func<T, V> f 作为你的 queryable-based 方法的参数,这意味着任何时候你调用 Select 或类似并传入 f,您将调用 Enumerable.Select 而不是 Queryable.Select。要真正正确地使用 IQueryable<>,您应该接受 Expression<Func<T, V>> f。到那时,您将不需要调用 AsQueryable

根据您使用的是 LINQ to Objects 还是不同的 LINQ 提供程序(例如 LINQ to SQL),您的两种方法 "should" 采用截然不同的路径,并且无法隐藏作为一个纯粹的实现细节,没有明显的变化,这可能会使它不如你想要的那样有用。