使用object和list <object>参数重载两个函数

Beh*_*rsi 12 c#

考虑以下代码:

static void Main(string[] args)
    {
        Log("Test");//Call Log(object obj)
        Log(new List<string>{"Test","Test2"});;//Also Call Log(object obj)
    }

    public static void Log(object obj)
    {
        Console.WriteLine(obj);
    }

    public static void Log(List<object> objects)
    {
        foreach (var obj in objects)
        {
            Console.WriteLine(obj);
        }
    }  
Run Code Online (Sandbox Code Playgroud)

在第一行中,我使用字符串值调用log并调用Log(object obj)但在第二行中我Log使用字符串列表new List<string>{"Test","Test2"}调用但编译器调用Log(object obj)而不是Log(List<object> objects).

为什么编译器有这种行为?

如何用字符串列表调用第二个日志?

Mar*_*ell 29

List<string> 不是一个List<object>; 然而,List<string> 一个object-因此它可以完美的选择超载.尝试改为:

public static void Log<T>(IList<T> objects)
{
    foreach (var obj in objects)
    {
        Console.WriteLine(obj);
    }
}  
Run Code Online (Sandbox Code Playgroud)

甚至:

public static void Log<T>(IEnumerable<T> objects)
{
    foreach (var obj in objects)
    {
        Console.WriteLine(obj);
    }
}  
Run Code Online (Sandbox Code Playgroud)

你可能还喜欢:

public static void Log(params object[] objects)
{
    foreach (var obj in objects)
    {
        Console.WriteLine(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

可以用作:

Log("Test","Test2");
Run Code Online (Sandbox Code Playgroud)

  • 你知道原因,我知道原因,`List <string>`不是`List <object>`但是我打赌OP不知道为什么,我想一个解释原因的链接(可能是另一个SO问题)协方差)会很有帮助. (5认同)

Ian*_*ths 7

Marc Gravell答案的一个变种:

public static void Log(IReadOnlyList<object> objects)
{
    foreach (var obj in objects)
    {
        Console.WriteLine(obj);
    }
}
Run Code Online (Sandbox Code Playgroud)

这不会为此特定示例添加任何内容,但如果您希望像使用a一样使用对集合的索引访问,则可以以List<T>不这样做的方式执行此IEnumerable<object>操作.(是的,有一个LINQ运算符用于对可枚举的索引访问,但它很笨拙,也可能非常慢.IReadOnlyList<object>如果你想明确你的方法需要有效的索引访问,那么它很有用.)

就像使用马克的版本IEnumerable<object>作为参数类型,这种利用协方差-不像List<T>,T是协变IEnumerable<T>IReadOnlyList<T>.这意味着因为string是一个object,一个IEnumerable<string>是一个IEnumerable<object>,同样IReadOnlyList<string>也是一个IReadOnlyList<object>.

两个接口的只读性质很重要.你的原始例子失败的全部原因是List<T>支持阅读和写作 - 如果你传递给我一个List<object>我可以添加任何东西 - a string,a Giraffe或任何我喜欢的东西.这就是为什么a List<string>不是一个可接受的替代品List<object>- 我无法添加Giraffe到a List<string>,即使我可以添加一个List<object>.但是因为IEnumerable<T>并且IReadOnlyList<T>不允许将对象添加到它们所代表的集合中,所有这些都是你可以得到的,而不是你可以放入的东西.任何来自只包含string对象的集合的object东西都是因为一切都是object.

是的,我知道您的原始代码并未尝试向列表中添加任何内容,但这并不重要 - 在这种情况下,所有C#都关心函数签名的外观.通过指定IReadOnlyList<object>你明确表示你永远不会尝试修改列表,此时C#知道可以通过List<string>.