我能为IEnumerable<T>提供不同于<T>的扩展方法吗?

3
我有一个扩展方法,可以作用于任何类,但如果我在处理 IEnumerable<T> 时,我想调用一个特殊版本。
例如:
public static class ExtensionMethods
{

    public static dynamic Test<T>(this T source)
    {   
        dynamic expandoObject = new System.Dynamic.ExpandoObject();
        var dictionary = (IDictionary<string,object>)expandoObject;

        dictionary["Test"] = source.ToString();

        return dictionary;
    }

    public static IEnumerable<dynamic> Test<T>(this List<T> source)
    {
        var result = new List<dynamic>();
        foreach(var r in source)
            yield return r.Test();          
    }


    public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source)
    {
        var result = new List<dynamic>();
        foreach(var r in source)
            yield return r.Test();          
    }
}   

// 使用方法

public class X 
{
    string guid = Guid.NewGuid().ToString();
}


void Main()
{
    List<X> list = new List<X>() { new X() };

    list.Test().Dump();                     // Correct but only works because there is an explicit overload for List<T>

    var array = list.ToArray();
    ((IEnumerable<X>) array).Test().Dump(); // Correct

     array.Test().Dump(); // Calls the wrong extension method
}

有没有办法让array.Test()调用IEnumerable版本而不必显式转换?

另外,如果我给扩展方法起不同的名称,是否有办法在不小心使用错误的方法时得到编译器错误提示?


数组继承自IEnumerable吗? - Ehsan Sajjad
你可以尝试使用 AsEnumerable(),但这只是将数组转换为 IEnumerable 的最简单和最优雅的方式:`array.AsEnumerable().Test();` - Farhad Jabiyev
数组实现了 IEnumerable<T>,但是实现是在运行时添加的,这就是为什么会调用“错误”的扩展方法。 - Magnus
可能是 https://dev59.com/ZlrUa4cB1Zd3GeqPmK2D 的重复问题。 - Pieter Geerkens
3个回答

1
我认为你正在错误的方向上尝试解决它。List实现了IEnumerable接口,因此编译器可能会在解决应调用哪个最佳方法时出现问题。你可以在扩展方法中测试IEnumerable是否是一个List。
public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source)
{
    if (source is List<T>) {
        // here 
    }
    var result = new List<dynamic>();
    foreach(var r in source)
        yield return r.Test();          
}

我不想为List<T>和IEnumerable<T>创建不同的版本。我只是添加了List<T>版本,以表明如果我为List<T>创建一个显式重载,它将被正确调用。如果我为T[]创建一个显式重载,它将适用于数组,但我无法为实现IEnumerable<T>的每个可能的类创建显式重载。 - sgmoore
抱歉,我当然是指您可以测试源是否为数组 if (source 是 T[]) - W.F.
仍然没有理解 IEnumerable<T> 扩展方法没有被调用的要点。 - sgmoore

0

您可以指定T,而不依赖于类型推断,这将提示编译器使用正确的扩展方法。代码看起来像这样:

var array = list.ToArray();
array.Test<X>().Dump();

发生的情况是,编译器无法确定要使用哪个扩展名,因为“Array”是两种方法签名的有效参数。
public static dynamic Test<T>(this T source) { .. }

public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source) { .. }

在第一种情况下,编译器可以假定 T 是类型为 Array 的。因此,编译器必须选择一个(可能是第一个定义的?)。

那样做是可行的,但它并没有强制指定T,因此仍会编译不正确的代码。 - sgmoore
你说得对,不幸的是我不知道在这种情况下是否可能实现编译时错误。但是你可以像下面答案中提到的那样添加另一个扩展方法。 - Marian Polacek

0

添加这个扩展方法以显式捕获所有数组类型:

public static IEnumerable<dynamic> Test<T>(this T[] source)
{
    var result = new List<dynamic>();
    foreach(var r in source)
        yield return r.Test();          
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接