C# 使用对象和列表重载两个函数<;对象>;参数
考虑以下代码:C# 使用对象和列表重载两个函数<;对象>;参数,c#,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(ob
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);
}
}
static void Main(字符串[]args)
{
日志(“测试”);//调用日志(对象obj)
日志(新列表{“Test”,“Test2”});//也调用日志(objectobj)
}
公共静态无效日志(对象obj)
{
控制台写入线(obj);
}
公共静态无效日志(列出对象)
{
foreach(对象中的var obj)
{
控制台写入线(obj);
}
}
在第一行中,我用字符串值调用log,它调用log(objectobj)
,但在第二行中,我用字符串列表newlist{“Test”,“Test2”}
调用log(objectobj)
而不是log(list objects)
为什么编译器有这种行为?
如何使用字符串列表调用第二个日志;然而,列表
是一个对象
——因此选择该重载非常有意义。请尝试:
public static void Log<T>(IList<T> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
它可以用作:
Log("Test","Test2");
列表
不能强制转换为列表
。
如果您有一个列表
,您可以向其中添加任何类型的对象。
如果您有一个列表
,则只能向其中添加字符串。
因此,
列表
不能转换为列表
,因为它不能以相同的方式使用。我想这是一个很好的示例。LSP在其简单的解释中声称,如果动物能咬人,那么狗(动物)也应该能咬人
这就像逻辑中的三段论,它指出:
公共作废日志(对象obj){}
)列表
是一个对象List
可用作该方法的参数,并记录Marc Gravell答案的一个变体:
public static void Log(IReadOnlyList<object> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
publicstaticvoidlog(IReadOnlyList对象)
{
foreach(对象中的var obj)
{
控制台写入线(obj);
}
}
这并没有给这个特定的示例添加任何内容,但是如果您想像使用列表那样使用对集合的索引访问,这允许您以IEnumerable
所没有的方式来执行。(是的,有一个LINQ操作符用于对可枚举项进行索引访问,但它很笨拙,而且速度可能非常慢。IReadOnlyList
如果您想明确说明您的方法需要高效的索引访问,那么它很有用。)
与Marc使用IEnumerable作为参数类型的版本一样,它利用了协方差-与List
不同,T
在IEnumerable
和IReadOnlyList
中是协方差的。这意味着因为字符串
是一个对象
,所以IEnumerable
是一个IEnumerable
,同样地,IReadOnlyList
也是一个IReadOnlyList
两个接口的只读特性都很重要。您最初的示例失败的全部原因是列表
支持读写-如果您给我一个列表
,我可以添加任何内容-一个字符串
,长颈鹿
或任何我喜欢的东西。这就是为什么列表
不能替代列表
——我不能将长颈鹿
添加到列表
,即使我可以将其添加到列表
。但是,由于IEnumerable
和IReadOnlyList
不允许将对象添加到它们所表示的集合中,所以重要的是您可以从中获取什么,而不是输入什么。任何来自只包含字符串
对象的集合的内容都将成为对象
,因为所有内容都是对象
是的,我知道您的原始代码没有尝试向列表中添加任何内容,但这并不重要——在本例中,C#关心的是函数签名的外观。通过指定IReadOnlyList
可以清楚地表明您永远不会试图修改列表,此时C#知道可以通过list
传递包含字符串Log的对象列表(新列表{“Test”,“Test2”})
你知道列表
和列表
是不同的类型,对吗?不过,关于差异有一个有趣的旁注:字符串[]
与对象[]
具有相同的特性,但是字符串[]
可以转换为对象[]
。如果通过索引器推入非字符串
值,则会在运行时出错(ArrayTypeMismatchException
)。但它允许您在编译时执行。这解释了为什么对象
重载是有效的,但根据编译器的说法,这并不是为什么它比列表
重载“更好”。而且没有人想解释“为什么”。我说过“更好”吗?不,你没有,这就是问题所在。在这种情况下,列表
重载不是有效的重载。因此,编译器拒绝将其作为选项,并通过重载解析将对象重载用作“更好”的重载。有几个步骤,包括确定哪些重载是有效的,从中选择“最好的”,然后继续编译该重载。你在第一步的中途停了下来。你的分析是不完整的,即使它不是不正确的。@Saeednamati:+1,:-)但是这个答案指向------------>你知道原因,我也知道原因,List
不是List
,但我打赌OP不知道为什么,我想有一个链接可以解释为什么(可能是到另一个关于协方差的问题)这会很有帮助。函数没有理由是泛型的——虽然List不能转换为List,但它可以转换为IEnumerable[a]
Log("Test","Test2");
public static void Log(IReadOnlyList<object> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}