c# -定义泛型函数重载的优先级

本文关键字:重载 优先级 函数 泛型 定义 | 更新日期: 2023-09-27 18:18:23

我有一个非常简单的递归定义的函数,它打印出任何列表的内容,定义如下:

    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[]。是否有可能为某些重载提供比其他重载更高的优先级,以便编译器会在其他重载之前自动尝试这些重载?

c# -定义泛型函数重载的优先级

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

至于发送List<List<T>>,你需要一个不同的过载,一个不会接受它。

通用解析似乎有点奇怪。我认为,如果一个对象实现了通用的IEnumerable<T>,那么这个匹配将比仅仅匹配通用的<T>更具体,但显然,如果你有一个List<T>,编译器会优先匹配<T>而不是IEnumerable<T>。我发现其他人在这里遇到了类似的问题,他们的解决方案是超载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"]]