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

时间:2018-02-22 10:23:53

标签: c# generics extension-methods

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

以下工作正常:

IQueryable<T>

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

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的重复实现,所以我把它重构为:

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

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

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);
    }
}

好的,执行错误要求对此代码行有效:

// 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();

在这里,我正在

  

CS0311类型&#39; System.Collections.Generic.IEnumerable&#39;不能用作类型参数&#39; E&#39;在泛型类型或方法&#39; Extensions.MySelect2(E,Func)&#39;。 “System.Collections.Generic.IEnumerable”&#39;没有隐式引用转换。到&#39; System.Linq.IQueryable&#39;。

为什么呢?它怎么能修复?请帮忙。

1 个答案:

答案 0 :(得分:11)

  

为什么呢?

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

where E : System.Linq.IQueryable<T>
  

怎么能修好?

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

您尝试实现的“简化”存在一个根本问题:您在原始MySelect1方法中实际上并没有完全重复。第一个调用AsEnumerable(),第二个调用AsQueryable()。你试图用演员代替那些演员,而这只是不起作用。

还有一个问题,即使使用原始方法:您接受Func<T, V> f作为基于可查询的方法的参数,这意味着您在调用Select或类似内容并传入{时{1}},您将致电f而不是Enumerable.Select。要正确使用Queryable.Select,您应该接受IQueryable<>。此时,您无论如何都不需要致电Expression<Func<T, V>> f

根据您是使用LINQ to Objects还是使用不同的LINQ提供程序(例如LINQ to SQL),您的两种方法“应该”采用截然不同的路径,并且无法在没有重大更改的情况下将其隐藏为纯粹的实现细节这可能会使它没有你想要的那么有用。