显然,C#容易受到'>>'的影响lexer困境as is C++。
这个C#代码非常有效,它编译并运行得很好:
var List = new Dummy("List");
var Nullable = new Dummy("Nullable");
var Guid = new Dummy("Guid");
var x = List<Nullable<Guid>> 10;
var y = List<Nullable<Guid>> .Equals(10,20);
你必须重载'&lt;'和'&gt;&gt;'上面的虚拟课程的操作员。
但编译器设法猜测在'x'的情况下,意思是使用List,Nullable和Guid局部变量。在'y'的情况下,它突然决定将它们视为众所周知的类型的名称。
以下是另一个例子的更详细说明: http://mihailik.blogspot.co.uk/2012/05/nested-generics-c-can-be-stinky.html
问题是:C#编译器如何解析'a&lt; b&lt; b&lt; c&gt;&gt;'算术表达式或泛型类型/方法?
当然,它不会尝试对程序文本进行多次“运行”,直到成功为止,或者是否成功?这需要无限前瞻,而且非常复杂。
答案 0 :(得分:5)
我已被引导到C#语言规范中的第7.6.4.2段:
简单名称(第7.6.2节)和成员访问(第7.6.4节)的产生可能会导致表达式语法含糊不清。
...
如果一个标记序列可以解析(在上下文中)作为简单名称(第7.6.2节),成员访问(第7.6.4节)或指针成员访问(第18.5.2节)结束使用type-argument-list(§4.4.1),紧跟在结束后的令牌&gt;令牌被检查。如果是
之一()]}:; ,。 ? ==!= | ^
然后将type-argument-list保留为simple-name,member-access或pointer-member-access的一部分,并且丢弃令牌序列的任何其他可能的解析。否则,类型参数列表不被视为简单名称,成员访问或指针成员访问的一部分,即使没有其他可能的令牌序列解析。请注意,在解析namespace-or-type-name(§3.8)中的type-argument-list时,不会应用这些规则。
因此,当涉及到type-argument-list时,确实会出现歧义,并且他们通过查找一个令牌来获得解决它的便宜方式。
它仍然是一个无限制的展望,因为'&gt;&gt;'之间可能有一兆字节的评论并且跟随令牌,但至少规则或多或少是清楚的。最重要的是,不需要推测深度解析。
答案 1 :(得分:-2)
编辑:我坚持认为没有歧义:
在你的例子中,根本没有歧义。永远不能将其评估为List<Guid?>
。上下文(额外的10)显示了编译器如何解释它。
var x = List<Nullable<Guid>> 10;
编译器会编译吗?:
var x = List<Guid?> 10;
很明显它不会。所以我还在寻找模糊性。
OTOH,第二个表达:
var y = List<Nullable<Guid>> .Equals(10,20);
必须将评估为List<Guid?>
,因为您正在调用.Equals
方法。同样,这可以用任何其他方式解释。
根本没有悖论。编译器完美地解析它。我仍然想知道apradox是哪个。
你犯了一个大错误。编译器解释整个表达式,并使用语言语法来理解它们。它不像你正在做的那样查看代码片段,而不考虑表达式的其余部分。
这些表达式根据to C# grammar进行解析。并且语法足够清楚,可以正确地解释代码。即在
var x = List<Nullable<Guid>> 10;
很明显10是文字。如果你跟进语法,你会发现:10是* 文字,所以它是* primary-no-array-creation-expression ,这是一个* < em> primary-expression ,这是一个* 一元表达式,它是一个* 乘法表达式,它是一个* 加法表达式。如果你在* &gt;&gt; 的右侧寻找一个加法表达式,你会发现它必须是一个* shift-expression ,所以在左侧* &gt;&gt; 必须解释为* additive-expression ,依此类推。
如果你能找到一种不同的方法来使用语法并为同一个表达式得到不同的结果,那么,我必须同意你的意见,但让我不同意!
最后:
由于:
List<Nullable<Guid>>
并按我们的意愿解释它们List<Nullable<Guid>>
。他们采用整个表达式并将其与语言语法相匹配。