C# 为什么我不能在扩展方法中用泛型类型变量替换IEnumerable?

C# 为什么我不能在扩展方法中用泛型类型变量替换IEnumerable?,c#,generics,extension-methods,C#,Generics,Extension Methods,我试图使扩展方法更通用,以避免某些实际代码的冗余,下面的代码只是为了演示这个问题-我的想法是使该方法也可用于IQueryable 以下工作很好: public static class Extensions { public static IEnumerable<T> MySelect1<T, V>(this IEnumerable<T> query, Func<T, V> f) { // do something,

我试图使扩展方法更通用,以避免某些实际代码的冗余,下面的代码只是为了演示这个问题-我的想法是使该方法也可用于IQueryable

以下工作很好:

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;
    }
}
现在我想摆脱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);
    }
}
<>这也是编译的,但是我不能用和上面一样的方法使用MyStReT2,考虑下面的内容:

// 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类型“System.Collections.Generic.IEnumerable”不能用作泛型类型或方法“Extensions.MySelect2E,Func”中的类型参数“E”。没有从“System.Collections.Generic.IEnumerable”到“System.Linq.IQueryable”的隐式引用转换

为什么??如何修复呢?请帮忙

为什么?

正是由于错误消息中所述的原因:您试图使用IEnumerable作为E的类型参数,但E有以下约束:

where E : System.Linq.IQueryable<T>
如何修复呢

不可能,假设我理解你想要达到的目标

您试图实现的简化有一个基本问题:您的原始MySelect1方法实际上没有完全重复。第一个调用AsEnumerable,第二个调用AsQueryable。你试图用演员阵容来替换这些角色,但那是行不通的

即使使用原始方法,也存在另一个问题:接受Func f作为基于queryable的方法的参数,这意味着任何时候调用Select或类似函数并传入f时,都将调用Enumerable.Select而不是queryable.Select。要真正正确地使用IQueryable,应该接受表达式f。在这一点上,您无论如何都不需要调用AsQueryable


您的两种方法应该根据您是使用LINQ to对象还是使用不同的LINQ提供程序(如LINQ to SQL)而采取完全不同的路径,并且如果没有可能使其不如您所希望的那样有用的重大更改,则不能将其作为纯实现细节隐藏。

其中e:System.LINQ.IQueryable,System.Collections.Generic.IEnumerable第二个约束不是多余的,因为IQueryable是IEnumerable?@HimBromBeere-您是对的,可查询对象也是可枚举的。但是我试图删除IQueryable,在本例中,第一条语句myQuery.AsQueryable.MySelect2。。。正在引发运行时错误,无法强制转换对象…感谢您提供的详细答案。我有一个问题:你写的E有这个约束,其中E:System.Linq.IQueryable。实际上,我有两个用逗号分隔的约束-它还应用了System.Collections.Generic.IEnumerable。根据我的理解,如果你有两个约束,那么它可以是第一个或第二个,即IQueryable或IEnumerable,这就是它背后的思想。“我说得对吗?”马特:不,你说得不对。如果存在多个约束,则类型参数必须满足所有约束。关于.AsQueryable和.AsEnumerable-这只是为了演示,以确保我们有一个可查询或可枚举的对象。我还注意到,如果我完全删除MySelect2中的constant,它甚至不再适用于可查询的代码行-因为.Select不再为人所知。CS1929“E”不包含“Select”的定义…@Matt:是的,这将失败。但重要的是,您需要根据它是否实际是可查询的调用两个不同的方法-Enumerable.Select和queryable.Select是具有不同参数类型的完全不同的方法。如果删除IQueryable约束,但保留IEnumerable约束,则可以编译,但基本上是使用LINQ to对象来处理所有问题,这不是一个好主意-您希望在数据库中进行尽可能多的工作。@Matt:从根本上讲,很难区分什么只是为了演示目的,什么是真实代码,部分原因是您没有解释真实代码的用途-为什么需要这些扩展方法。让这个问题更清楚一些会有所帮助。
where E : System.Linq.IQueryable<T>