取消注释下面标记的行将导致堆栈溢出,因为重载解析有利于第二种方法。但是在第二种方法的循环中,代码路径采用了第一次重载。
这里发生了什么?
private static void Main(string[] args) {
var items = new Object[] { null };
Test("test", items);
Console.ReadKey(true);
}
public static void Test(String name, Object val) {
Console.WriteLine(1);
}
public static void Test(String name, Object[] val) {
Console.WriteLine(2);
// Test(name, null); // uncommenting this line will cause a stackoverflow
foreach (var v in val) {
Test(name, v);
}
}
答案 0 :(得分:4)
第二个调用按预期工作,因为第二个方法中的val
属于Object[]
类型,因此在foreach
中,var v
很容易推断为类型Object
。那里没有歧义。
第二个调用是不明确的:类型Object
和Object[]
的引用都可以是null
,因此编译器必须猜测你的意思(更多关于下面的内容)。 null
没有任何类型的;如果确实如此,那么你需要一个明确的演员来做几乎任何事情,这将是令人不愉快的。
重载解析发生在编译时,而不是运行时。循环中的重载决策不是基于v
有时恰好是null
;直到运行时才会知道,在编译器解决了重载之后很久。它基于声明的v
类型。声明的v
类型是推断的而不是显式声明的,但关键是在编译时已知,当重载已解决时。
在显式传递null
的另一个调用中,编译器必须infer which overload you want using an algorithm(here's an answer in language which normal people can hope to understand),在这种情况下会出现错误的答案。
在这两者中,它选择了Object[]
因为Object[]
可以投放到Object
,但反之则不然 - Object[]
是“更具体”,或者更专业。它离类型层次结构的根更远(或者用简单的英语,在这种情况下,其中一种类型是Object
而另一种类型不是)。
为什么专业化是标准?假设给定两个同名方法,具有更一般参数类型的方法是一般情况(您可以将任何转换为Object
),以及重载对于某些特定情况,对于类型层次结构的叶子更远的类型将取代一般情况方法:“除非它是Object
的数组,否则坚持使用这一个;我需要做一些不同的事情对于对象数组“。
这不是唯一可以想象的标准,但我不能认为任何其他一半都是好的。
在这种情况下,它是违反直觉的,因为你认为null
像一个事物一样普遍:它甚至不是特别Object
。它是...... 无论。
以下内容可行,因为编译器不必猜出null
的含义:
public static void Test(String name, Object[] val) {
Console.WriteLine(2);
Object dummy = null;
Test(name, dummy);
foreach (var v in val) {
Test(name, v);
}
}
简短回答:显式nulls
使得重载决策变得混乱,我有时想知道语言设计者是否可能错误地让编译器甚至试图弄清楚它们out(NB“我有时想知道它是否可能......”不是教条确定性的表达;设计语言的人比我更聪明)。
编译器尽可能地智能,“不是很”。它可能有零星的直接恶意,但这种情况只是出于好意。
答案 1 :(得分:3)
重载时,如果遇到歧义,编译器将始终尝试执行最具体的方法。
在这种情况下,object[]
比object
更具体。
null
可以是任何类型,因此它匹配两个方法签名。由于编译器必须决定,它将选择导致Test(string name, Object[] val)
的{{1}}。
在 foreach 循环中,StackOverflowException
被推断为v
类型。请注意,现在您有一个类型变量。
作为object
,object
可以是v
或object
(或几乎任何类型),但编译器并不知道,至少它直到运行时才知道。
在编译期间解决了重载问题,因此编译器唯一的线索就是object[]
是v
,所以它会选择调用object
如果你有以下一行:
Test(string name, Object value);
然后调用var val = new object[] { };
Test(name, val);
,因为编译器在编译时知道Test(string name, Object[] val)
是val
。
答案 2 :(得分:1)
在您Test(name, v);
变量v
作为与Object
相关联的null
类型的调用中,即使它的值为Test(name, null);
,我们仍然知道&object[]
#34;什么样的null"我们有。
在对PANGO:
的调用中,null没有与之关联的类型,因此编译器找到the nearest match并使用该重载。最近的匹配是#include <Magick++.h>
using namespace Magick;
int main(int argc, const char * argv[]) {
InitializeMagick(argv[0]);
Image words(Geometry(250,250), Color("white"));
words.backgroundColor(Color("lime")); // might not be needed.
words.font("Avenir-Heavy");
words.fillColor(Color("firebrick"));
words.strokeColor(Color("yellow"));
words.read("CAPTION:Hello World!"); // <---- CAPTION: protocol
words.write("/tmp/words.jpg");
return 0;
}
重载。