解析器前沿的泛型和挑战

时间:2013-08-06 04:26:53

标签: c# generics

如果你有:

F(G<A,B>(4));

这是否意味着用户想要通过比较G和A以及B和常数4来生成2个参数来调用方法F?

或者它是否意味着使用类型参数A和B以及参数4调用泛型方法G的结果调用F?

2 个答案:

答案 0 :(得分:7)

所以我试了一下才确定。事实证明这很好用:

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'不是通用方法。如果您打算使用表达式列表,请在&lt;周围使用括号。表达

所以答案是表达式F(G<A,B>(4))被解释为泛型函数调用。有许多方法可以强制编译器将其视为两个参数的单个函数调用:F(G<A,B>4)F((G)<A,B>(4))F(G>A,B>(4)),仅举几例。

答案 1 :(得分:6)

你应该阅读C#规范的7.6.4.2,该规范处理语法歧义并且几乎逐字地讨论这个例子。引用:

  

如果一个标记序列可以解析(在上下文中)作为简单名称(第7.6.2节),成员访问(第7.6.4节)或指针成员访问(第18.5.2节)结束使用type-argument-list(§4.4.1),将检查紧跟在结束>标记之后的标记。如果是

之一      

()]}:; ,。 ? ==!= | ^

     

然后将type-argument-list保留为simple-name,member-access或pointer-member-access的一部分,并且丢弃令牌序列的任何其他可能的解析。

这里,G是一个简单的名称,问题是<A,B>是否被解释为类型参数列表,作为这个简单名称的一部分。

(后面有>,因此片段G<A,B>是方法的简单名称。该方法是一个泛型方法,类型参数为AB,参数为4. F因此是一个带有单个参数的方法。

需要注意的一件有趣的事情是,如果解析失败,编译器不会考虑任何替代方案。正如您在p.s.w.g.的答案中所看到的,即使唯一有效的解释是F是一个带有两个参数的方法,也不会考虑它。