C# - 定义泛型函数重载的首选项

Posted

技术标签:

【中文标题】C# - 定义泛型函数重载的首选项【英文标题】:C# - Define preference for generic function overloads 【发布时间】:2017-04-01 10:55:32 【问题描述】:

我有一个非常简单的递归定义函数,可以打印出任何定义的列表的内容;

    static string ShowList<T>(IEnumerable<T> iterable)
    
        return "[" + string.Join(", ", iterable.Select(e => ShowList(e))) + "]";
    

    static string ShowList(string str)
    
        return $"\"$str\"";
    

    static string ShowList<T>(T elem)
    
        return elem.ToString();
    

如您所见,最低覆盖是适用于任何参数的 catch all。最高重载仅适用于可枚举的参数。理想情况下,我希望它首先检查并运行最具体的重载,然后如果它们都失败了,请使用一般情况。但我发现它总是直接针对一般情况,并且只打印出Systems.Generic.Containers.List1[]。是否可以赋予某些重载比其他重载更高的优先级,以便编译器会在其他重载之前自动尝试这些重载?

【问题讨论】:

我不认为你的函数在定义上是递归的 @Jonesopolis 查看第 3 行的递归调用 我猜如果 T 本身是 IEnumerable 的话,好吧 @Maurdekye 不,它不是递归的。一个重载调用另一个重载。任何重载都不能调用自己,因此该函数实际上不是递归的。 @Maurdekye 多个重载不能应用于单个值。正如我在之前的评论中解释的那样,从有问题的调用站点中,只有一个有效的重载,因此使用了重载。此外,如果存在 多个适用的重载(在您的代码中从未发生过), 实际上对于重载的“更好”有严格的规则,你如果您想了解它们是什么,可以免费查看语言规范。它们不是“随机”选择的。 【参考方案1】:

在我看来,因为 e(在 iterable.Select 的委托中)是 T 类型,因此编译器将使用 T 类型重载。我建议,由于它所做的只是返回 ToString() 方法,因此将其删除并将委托中的调用更改为 ShowList(e.ToString())。如果 ToString() 被重载,您很可能会获得有意义的数据,否则您将获得对象类型数据。

至于发送List&lt;List&lt;T&gt;&gt;,你需要一个不同的过载,人们不会接受它。

【讨论】:

【参考方案2】:

通用分辨率似乎有点奇怪。我认为如果一个对象实现了泛型IEnumerable&lt;T&gt;,那么这个匹配将比仅仅匹配泛型&lt;T&gt;更具体,但显然如果你有一个List&lt;T&gt;,编译器会优先匹配&lt;T&gt;而不是IEnumerable&lt;T&gt;。我发现其他人遇到了类似的问题here,他们的解决方案是用List&lt;T&gt; 重载,但这似乎不是一个好的解决方案,因为那样你就会重载实现IEnumerable&lt;T&gt; 的所有东西。我发现了一个使用 is 运算符的简单解决方法

    static string ShowList<T>(IEnumerable<T> iterable)
    
        return "[" + string.Join(", ", iterable.Select(e => ShowList(e))) + "]";
    

    static string ShowList<T>(T elem)
    
        if (elem is IEnumerable<object> iterable)
        
            //This will force the call to the overload for IEnumerable<T>
            return ShowList(iterable);
        
        else
        
            //This call will implicitly call ToString, so it covers the generic
            //object case that needs to call ToString and the case where it is
            //already a string, so the string overload is not needed.
            return $"\"elem\"";
        
    

这里是测试这些方法的代码,故意将 List 与数组混合,以表明它适用于实现 IEnumerable&lt;T&gt; 的不同项目

var list = new List<string[]>()new string[2]"hello", "world", new string[2]"foo", "bar";
Console.WriteLine(ShowList(list)); //outputs: [["hello", "world"], ["foo", "bar"]]

【讨论】:

以上是关于C# - 定义泛型函数重载的首选项的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用自定义的泛型函数/方法对泛型数组进行四则运算

使用不在参数中的第一个泛型类型重载泛型函数

表达式树练习实践:C#值类型引用类型泛型集合调用函数

Typescript核心篇——函数_this指向—重载—泛型

泛型函数的重载可以对其他重载开放吗?

C#怎么定义泛型集合类型的函数,返回一个集合