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<List<T>>
,你需要一个不同的过载,人们不会接受它。
【讨论】:
【参考方案2】:通用分辨率似乎有点奇怪。我认为如果一个对象实现了泛型IEnumerable<T>
,那么这个匹配将比仅仅匹配泛型<T>
更具体,但显然如果你有一个List<T>
,编译器会优先匹配<T>
而不是IEnumerable<T>
。我发现其他人遇到了类似的问题here,他们的解决方案是用List<T>
重载,但这似乎不是一个好的解决方案,因为那样你就会重载实现IEnumerable<T>
的所有东西。我发现了一个使用 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<T>
的不同项目
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# - 定义泛型函数重载的首选项的主要内容,如果未能解决你的问题,请参考以下文章