[英]Why can't I replace IEnumerable<T> by a generic type variable in extension method?
I am trying to make an extension method more generic to avoid redundancy ( Here is an example of some real code, the code below is just to demonstrate the issue - I had the idea to make the method available for IQueryable<T>
as well). 我试图使扩展方法更通用以避免冗余( 这是一些实际代码的示例 ,下面的代码只是为了演示问题 - 我有想法使该方法可用于
IQueryable<T>
) 。
The following works fine: 以下工作正常:
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;
}
}
I can use it in LinqPad like (when connected with Northwind database): 我可以在LinqPad中使用它(当与Northwind数据库连接时):
var myQuery=(from x in Customers select x);
myQuery.AsEnumerable().MySelect1(d => d.CustomerID).Dump();
myQuery.AsQueryable().MySelect1(d => d.CustomerID).Dump();
Now I wanted to get rid of the duplicate implementation of MySelect1, so I refactored it as: 现在我想摆脱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);
}
}
This compiles too, but I cannot use MySelect2 the same way as I did above, consider the following: 这也编译,但我不能像上面那样使用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();
Ok, doing what the error asks for works for this code line: 好的,做错误要求对此代码行有效:
myQuery.AsQueryable()
.MySelect2<IQueryable<Customers>, Customers, String>(d => d.CustomerID).Dump();
but not for that one: 但不是那个:
myQuery.AsEnumerable<Customers>()
.MySelect2<IEnumerable<Customers>, Customers, String>(d => d.CustomerID).Dump();
Here, I am getting 我来了
CS0311 The type 'System.Collections.Generic.IEnumerable' cannot be used as type parameter 'E' in the generic type or method 'Extensions.MySelect2(E, Func)'.
CS0311类型'System.Collections.Generic.IEnumerable'不能用作泛型类型或方法'Extensions.MySelect2(E,Func)'中的类型参数'E'。 There is no implicit reference conversion from 'System.Collections.Generic.IEnumerable' to 'System.Linq.IQueryable'.
没有从'System.Collections.Generic.IEnumerable'到'System.Linq.IQueryable'的隐式引用转换。
Why? 为什么? And how can it be fixed?
它怎么能修复? Please help.
请帮忙。
Why?
为什么?
For exactly the reason stated in the error message: you're trying to use IEnumerable<Customers>
as the type argument for E
, but E
has this constraint: 正是出于错误消息中所述的原因:您尝试使用
IEnumerable<Customers>
作为E
的类型参数,但E
具有以下约束:
where E : System.Linq.IQueryable<T>
And how can it be fixed?
它怎么能修复?
It can't, assuming I understand what you're trying to achieve. 它不能,假设我明白你想要实现的目标。
There's a fundamental problem with the "simplification" you're trying to achieve: you don't actually have full duplication in your original MySelect1
methods. 您尝试实现的“简化”存在一个根本问题:您在原始
MySelect1
方法中实际上并没有完全重复。 The first calls AsEnumerable()
and the second calls AsQueryable()
. 第一个调用
AsEnumerable()
,第二个调用AsQueryable()
。 You're trying to replace those with a cast, and that's just not going to work. 你试图用演员替换那些,但那是行不通的。
There's a further problem, even with your original methods: you're accepting Func<T, V> f
as a parameter for your queryable-based method, which means any time you call Select
or similar and passing in f
, you'll be calling Enumerable.Select
instead of Queryable.Select
. 还有一个问题,即使使用原始方法:您接受
Func<T, V> f
作为基于可查询的方法的参数,这意味着每当您调用Select
或类似并传入f
,您将成为调用Enumerable.Select
而不是Queryable.Select
。 To really use IQueryable<>
properly, you should accept Expression<Func<T, V>> f
instead. 要正确使用
IQueryable<>
,您应该接受Expression<Func<T, V>> f
。 At that point, you won't need to call AsQueryable
anyway. 此时,无论如何都不需要调用
AsQueryable
。
Your two methods "should" take radically different paths based on whether you're using LINQ to Objects or a different LINQ provider (eg LINQ to SQL), and that can't be hidden as a pure implementation detail without significant changes that would probably make it less useful than you want anyway. 根据您是使用LINQ to Objects还是使用不同的LINQ提供程序(例如LINQ to SQL),您的两种方法“应该”采用完全不同的路径,并且不能将其隐藏为纯粹的实现细节而不会发生可能的重大更改无论如何,它都没有你想要的那么有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.