考虑以下代码:
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);
}
}
在第一行中,我使用字符串值调用log并调用Log(object obj)
,但在第二行中,我使用字符串Log
的列表调用new List<string>{"Test","Test2"}
,但编译器调用Log(object obj)
而不是Log(List<object> objects)
。
为什么编译器有这种行为?
如何使用字符串列表调用第二个日志?
答案 0 :(得分:29)
List<string>
不是 a List<object>
;但是,List<string>
是object
- 因此选择超载非常有意义。请尝试改为:
public static void Log<T>(IList<T> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
甚至:
public static void Log<T>(IEnumerable<T> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
您可能也喜欢:
public static void Log(params object[] objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
可以用作:
Log("Test","Test2");
答案 1 :(得分:7)
Marc Gravell答案的变体:
public static void Log(IReadOnlyList<object> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
这不会向此特定示例添加任何内容,但如果您希望使用对List<T>
的索引访问权限,则可以IEnumerable<object>
的方式执行此操作没有。 (是的,有一个LINQ运算符用于对可枚举的索引访问,但它很笨拙,也可能非常慢。IReadOnlyList<object>
非常有用,如果你想明确你的方法需要有效的索引访问。)
与使用IEnumerable<object
&gt;的Marc版本一样作为参数类型,这会利用协方差 - 与List<T>
不同,T
在IEnumerable<T>
和IReadOnlyList<T>
中是协变的。这意味着,因为string
是object
,IEnumerable<string>
是IEnumerable<object>
,同样,IReadOnlyList<string>
是IReadOnlyList<object>
。
两个接口的只读性质很重要。原始示例失败的全部原因是List<T>
支持阅读和写作 - 如果您传递给我List<object>
我可以添加任何内容 - string
,Giraffe
或者我喜欢什么。这就是为什么List<string>
不能替代List<object>
的原因 - 我无法向Giraffe
添加List<string>
,即使我可以将List<object>
添加到IEnumerable<T>
1}}。但是因为IReadOnlyList<T>
和string
不允许将对象添加到它们代表的集合中,所以重要的是你可以得到的东西,而不是你可以放入的东西。任何来自集合的东西仅包含object
个对象的内容为object
,因为所有内容都为IReadOnlyList<object>
。
是的,我知道您的原始代码并未尝试向列表中添加任何内容,但这并不重要 - 在这种情况下,所有C#都关心函数签名的外观。通过指定List<string>
,您明确表示您永远不会尝试修改列表,此时C#知道可以通过{{1}}。
答案 2 :(得分:3)
List<string>
无法投放到List<Object>
。
如果您有List<Object>
,则可以向其添加任何类型的对象。
如果您有List<String>
,则只能为其添加字符串。
因此,List<String>
无法转换为List<Object>
,因为它不能以相同的方式使用。
答案 3 :(得分:1)
我想这是Liskov Substitution Principal的一个很好的例子。 LSP在其简单的解释中声称,如果动物可以咬人,那么一只狗(它是一种动物)也应该能够咬人。
这就像逻辑中的三段论,其中指出:
我想在这里,编译器遵循这个原则,因为:
public void Log (object obj) {}
)List<string>
是一个对象List<string>
可以用作该方法的参数,并进行记录。