【问题标题】:Nested generic syntax ambiguity >>嵌套通用语法歧义>>
【发布时间】:2012-05-16 00:06:40
【问题描述】:

显然,C# 很容易受到“>>”词法分析器困境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);

您必须为上面的 Dummy 类重载“>”运算符。

但编译器设法猜测在“x”情况下的含义是使用 List、Nullable 和 Guid 局部变量。在 'y' 的情况下,它突然决定将它们视为知名类型的名称。

这里有一个更详细的描述和另一个例子: http://mihailik.blogspot.co.uk/2012/05/nested-generics-c-can-be-stinky.html

问题是:C# 编译器如何将 'a>' 解析为算术表达式或泛型类型/方法?

在成功之前,它肯定不会尝试对程序的文本进行多次“遍历”,或者是吗?这将需要无限的前瞻,而且也非常复杂。

【问题讨论】:

  • “选择一个意思而不是另一个意思?” ... 你是什么意思?这里有一个单一的、明确的含义……唯一不明确的原因是变量名的选择。
  • 'a>' 的含义非常依赖于上下文。编程语言允许这样做是不寻常的。嗯,实际上主要问题是编译器如何决定它是算术移位还是泛型类型规范的一部分。
  • 如果你足够疯狂地超载 '>' 你应该期待你得到什么。
  • 这让我想起了the --&gt; and &lt;-- operators ;)
  • 这不是因为我疯狂到使用该语言的合法特性,而是关于编译器如何解决这个谜题。你我看语法,套用常识,但编译器需要明确的规则,没有常识。

标签: c# lexer nested-generics


【解决方案1】:

我已被定向到 C# 语言规范中的第 7.6.4.2 段:

http://download.microsoft.com/download/0/B/D/0BDA894F-2CCD-4C2C-B5A7-4EB1171962E5/CSharp%20Language%20Specification.htm

simple-name (§7.6.2) 和 member-access (§7.6.4) 的产生式可能会导致表达式语法中的歧义。

...

如果可以(在上下文中)将标记序列解析为简单名称(第 7.6.2 节)、成员访问(第 7.6.4 节)或指针成员访问(第 18.5.2 节)结尾使用类型参数列表(第 4.4.1 节),检查紧跟在关闭 > 标记之后的标记。如果是其中之一

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

然后类型参数列表作为简单名称、成员访问或指针成员访问的一部分保留,并且丢弃标记序列的任何其他可能的解析。否则,类型参数列表不被认为是简单名称、成员访问或指针成员访问的一部分,即使没有其他可能的标记序列解析。请注意,在解析命名空间或类型名称(第 3.8 节)中的类型参数列表时,不会应用这些规则。

因此,当涉及到 type-argument-list 时,确实可能会出现歧义,并且他们有一种廉价的方法来解决它,通过向前看一个令牌。

这仍然是一个无限制的展望,因为在“>>”和后面的标记之间可能有 1 兆字节的 cmets,但至少规则或多或少是清楚的。最重要的是,不需要推测性的深度解析。

【讨论】:

    【解决方案2】:

    编辑:我坚持没有歧义: 在您的示例中,根本没有歧义。这永远不能被评估为List&lt;Guid?&gt;。上下文(额外的 10 个)显示了编译器如何解释它。

    var x = List<Nullable<Guid>> 10;
    

    编译器会编译这个吗?:

    var x = List<Guid?> 10;
    

    显然不会。所以我仍在寻找歧义。

    OTOH,第二个表达式:

    var y =  List<Nullable<Guid>> .Equals(10,20);
    

    必须被评估为List&lt;Guid?&gt;,因为您正在调用.Equals 方法。同样,这可以用任何其他方式解释。

    根本没有悖论。编译器完美解析它。我仍然想知道其中的悖论是什么。

    你犯了一个大错误。编译器解释整个表达式,并使用语言语法来理解它们。它不会像您正在做的那样查看代码片段,而不考虑表达式的其余部分。

    这些表达式是根据to C# grammar 解析的。并且语法足够清晰,可以正确解释代码。 IE。在

    var x = List<Nullable<Guid>> 10;
    

    很明显,10 是一个字面量。如果你跟进语法,你会发现:10 是 *literal,所以它是 *primary-no-array-creation-expression,它是 *primary-expression,这是一个 *unary-expression,这是一个 *multiplicative-expression,这是一个 *additive-expression 。如果你在 *>> 的右边寻找一个加法表达式,你会发现它一定是一个 *shift-expression,所以 *>> 必须被解释为 *additive-expression 等等。

    如果你能够找到不同的方式来使用语法并为相同的表达式获得不同的结果,那么,我将不得不同意你,但让我不同意!

    最后:

    • 对人类来说非常令人困惑
    • 对编译器来说绝对清晰明确

    因为:

    • 我们人类识别模式,提取我们熟悉的整个文本片段,例如 List&lt;Nullable&lt;Guid&gt;&gt;,并按照我们的意愿解释它们
    • 编译器不会像我们一样解释代码,而是采用像List&lt;Nullable&lt;Guid&gt;&gt; 这样的熟悉片段。他们获取整个表达式并将其与语言语法相匹配。

    【讨论】:

    • 我说它可以编译是因为它可以编译。这段代码是我在 Visual Studio 2010 中编译并运行的。我知道这听起来不太合理,这就是为什么它如此令人不安的原因。 Dummy 类在那个链接之后,为了简洁起见,我在这里跳过它。
    • 我已经尝试了代码,var x = List &lt; Nullable &lt; Guid &gt;&gt; 10; 给出了错误“结构名称此时无效”,指的是 Nullable。我真的很想看到它工作,但我不能。你能展示整个代码,包括“使用”部分吗?我在你的博客上也看不到。
    • 嗯……太奇怪了。是 Rehsarper 出现了错误。但是 VS 会编译它。
    • @OlegMihailik:我很抱歉认为它无法编译。并挑战你去做。事实上,Resharper 也会误解代码并在上面显示错误,但 VS 会编译它。 (也 VS 2008)。
    • 没问题,如果我们没有犯错,我们就不需要 StackOverflow :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    相关资源
    最近更新 更多