简体   繁体   English

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

[英]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.

相关问题 我可以为IEnumerable使用不同的Extension方法吗? <T> 而不是 <T> ? - Can I have a different Extension method for IEnumerable<T> than for <T>? C#-为什么不能使IEnumerable具有通用接口类型? - C# - Why can't I make an IEnumerable of a generic interface type? 为什么我不能通过字典 <string, string> 到IEnumerable <KeyValuePair<string, string> &gt;作为通用类型 - Why can't i pass Dictionary<string, string> to IEnumerable<KeyValuePair<string, string>> as generic type 可以在扩展方法中使用通用T吗? - Can I use generic T in an extension method? 为什么IEnumerable(T)不被接受为扩展方法接收器 - Why is IEnumerable(of T) not accepted as extension method receiver 当类型参数为IEnumerable时,如何将扩展方法附加到泛型类 <T> ? - How could an extension method be attached to a generic class when the type argument is IEnumerable<T>? 通用扩展方法返回IEnumerable <T>而不使用反射 - Generic extension method returning IEnumerable<T> without using reflection 类型的通用方法 <T> 在运行时并将结果存储在IEnumerable中 <T> - Generic method of type <T> at runtime AND storing result in IEnumerable<T> 为什么不能将`this`转换为泛型? - Why can't I cast `this` to a generic type? IEnumerable <T>的泛型方法的重载? - Overload of generic method for IEnumerable<T>?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM