如果你有:
F(G<A,B>(4));
这是否意味着用户想要调用具有 2 个参数的方法 F,这些参数是比较 G 和 A,以及 B 和常数 4 的结果?
或者它是否意味着调用 F 并使用类型参数 A 和 B 以及参数 4 调用泛型方法 G 的结果?
所以我试了一下只是为了确定。事实证明这很好用:
void F(int x) { }
int G<T, U>(int x) { return x; }
class A { }
class B { }
void Main()
{
F(G<A,B>(4));
}
但这会产生一些编译错误:
void F(bool x, bool y) { }
void Main()
{
int G = 0, A = 1, B = 2;
F(G<A,B>(4));
}
找不到类型或命名空间名称“A”(按 F4 添加 using 指令或程序集引用)
找不到类型或命名空间名称“B”(您是否缺少 using 指令或程序集引用?)
变量“G”不是通用方法。如果您想要一个表达式列表,请在 < 表达式周围使用括号。
所以答案是表达式F(G<A,B>(4))
被解释为泛型函数调用。有许多方法可以强制编译器将其视为两个参数的单个函数调用:F(G<A,B>4)
、F((G)<A,B>(4))
或F(G>A,B>(4))
,仅举几例。
您应该阅读 C# 规范的 7.6.4.2,它处理语法歧义并几乎逐字讨论此示例。去引用:
如果可以(在上下文中)将标记序列解析为以类型结尾的简单名称(第 7.6.2 节)、成员访问(第 7.6.4 节)或指针成员访问(第 18.5.2 节) -argument-list(第 4.4.1 节),
>
检查紧跟在结束标记之后的标记。如果它是其中之一( ) ] } : ; , . ? == != | ^
然后类型参数列表被保留为简单名称、成员访问或指针成员访问的一部分,并且丢弃标记序列的任何其他可能的解析。
这里,G
是一个简单名称,问题是是否<A,B>
要被解释为类型参数列表作为这个简单名称的一部分。
(
后面有一个>
,所以片段G<A,B>
是方法的简单名称。该方法是具有类型参数的泛型方法,A
并且B
参数为 4。F
因此是具有单个参数的方法。
需要注意的一件有趣的事情是,如果解析失败,编译器不会考虑任何替代方案。正如您从 pswg 的回答中看到的那样,即使唯一有效的解释是一种F
采用两个参数的方法,也不会考虑它。