【问题标题】:Does the C# compiler get the Color Color rule wrong with const type members?C# 编译器是否对 const 类型成员的颜色规则错误?
【发布时间】:2015-02-27 21:57:20
【问题描述】:

好的,所以 C# 语言规范在 Color Color 规则上有 a special section (old version linked),其中成员及其类型具有相同的名称。知名大师 Eric Lippert once blogged 关于它。

我在这里要问的问题在某种意义上(不)与线程Circular definition in a constant enum 中提出的问题完全相同。如果您愿意,可以去投票其他问题。

现在回答我的问题。考虑这段代码:

namespace N
{
    public enum Color
    {
        Green,
        Brown,
        Purple,
    }

    public class C1
    {
        public const Color Color = Color.Brown;  // error CS0110 - WHY? Compiler confused by Color Color?
    }
    public class C2
    {
        public static readonly Color Color = Color.Brown;  // fine
    }
    public class C3
    {
        public static Color Color = Color.Brown;  // fine
    }
    public class C4
    {
        public Color Color = Color.Brown;  // fine
    }
}

这里的重点是,在上述每种情况下,最右边的标识符Color 可以引用enum 类型,也可以引用同名的类成员。但是上面提到的Color Color规则意味着我们应该看看成员(Brown)是静态的还是非静态的。由于在这种情况下它是静态的,我们应该相应地解释Color

我明显的主要问题:为什么这不适用于const 类型的成员?这是无意的吗?

(显然,说N.Color.BrownN 是命名空间)“修复”它;我不是在问这个!)


旁注:使用局部变量const,不存在上述异常:

    public class C5
    {
        public Color Color;
        void M()
        {
            const Color Color = Color.Brown;  // works (no warning for not using local variable?)
        }
    }
    public class C6
    {
        public Color Color;
        void M()
        {
            const Color other = Color.Brown;  // works (warning CS0219, 'other' not used)
        }
    }

【问题讨论】:

  • 伪枚举也有同样的问题:class Pseudo { public const Pseudo Brown = null; } class C7 { public const Pseudo Pseudo = Pseudo.Brown; /* error CS0110 */ }
  • 常量表达式很重要。从技术上讲,编译器可以通过在解析整个语句之前不将标识符添加到符号表来解决这个问题。但这会影响const int bad = bad + 1; 的诊断质量现在你会得到“名称不存在”,这并不漂亮。
  • 谷歌搜索了一下,在 JetBrains 上看到一个关于智能感知评论 youtrack.jetbrains.com/issue/RSRP-353104 的错误,在那里看到评论会很有趣
  • 您使用的是哪个编译器版本?我想知道这与 Roslyn (VS2015) 与早期版本是否有任何不同。
  • 我猜在以前的版本中是一样的。我发现了这个:connect.microsoft.com/VisualStudio/feedback/details/621384/…,它似乎在 2010 年被报道过,微软对此的回答是“不会修复”。

标签: c# language-lawyer member-access


【解决方案1】:

这是一个错误。我无法在 VS 2015 的 CTP 5 中重现该问题,我认为这个问题应该作为 Roslyn 重写的一部分进行修复。但是,下面的评论者指出,他们可以在 CTP 6 中重现它。所以我不确定这里发生了什么,至于这个错误是否已被修复。

就个人而言:我不记得当它在 2010 年首次报道时我是否负责调查这个问题,但由于我当时在圆形检测器上做了很多工作,所以几率相当大.

这远不是圆形检测器中唯一的错误;如果嵌套的泛型类型又具有泛型基类型,其类型参数涉及嵌套类型,那将变得相当混乱。

我一点也不惊讶亚历克斯“不会修复”这个问题。我花了很长时间重写进行类循环检测的代码,并且认为这种更改风险太大。所有这些工作都交给了罗斯林。

如果您有兴趣了解颜色颜色绑定代码在 Roslyn 中的工作方式,请查看恰当命名的方法 BindLeftOfPotentialColorColorMemberAccess -- 我喜欢一些描述性的方法名称 -- 在 Binder_Expressions.cs 中。

【讨论】:

  • 感谢埃里克。我是否正确地认为它同时使用了两个定义,这就是为什么它在您使用 const 之前一直有效?
  • @DrewJordan:我不记得了;我得看一下代码。不过,你的假设是完全合理的。
  • @EricLippert 我有一个 CTP 6 虚拟机,但它没有固定在那里。
  • @Pharylon:很奇怪,我在这里运行 CTP 5,但无法重现该问题。我会在正文中注明。
【解决方案2】:

1) 它不适用于const,因为它试图同时允许两个定义(枚举类型和类成员),因此它试图将自己定义为自身的函数。

2) 是无意的吗?有点。这是对预期行为的意外后果。

基本上,这是 Microsoft 承认但已归档为“不会修复”的错误,记录在 Connect here 上。

我在任何地方都找不到在线 5.0 语言规范(以文章或博客形式),但如果您有兴趣,可以下载它here。我们对第 161 页,第 7.6.4 节,会员访问感兴趣,它是第 7.6.4.1 节,与 OP 链接到的部分相同(当时是 7.5.4.1)。

您可以将成员和类型命名为完全相同的名称(例如 Color)这一事实是明确允许的,即使您的标识符现在具有两个不同的含义。这是规范的语言:

7.6.4.1 相同的简单名称和类型名称 在 E.I 形式的成员访问中,如果 E 是单个标识符,并且如果 E 的含义为 简单名称(第 7.6.2 节)是常量、字段、属性、局部变量、 或与 E 的含义相同类型的参数作为类型名称 (§3.8),那么 E 的两种可能含义都是允许的。他们俩 E.I 的可能含义永远不会模棱两可,因为我必须 在这两种情况下都是类型 E 的成员。换句话说,规则 只允许访问 E 的静态成员和嵌套类型 否则会发生编译时错误。例如:

struct Color {  
    public static readonly Color White = new Color(...);    
    public static readonly Color Black = new Color(...);    
    public Color Complement() {...} 
} 
class A {   
    public Color Color;                 // Field Color of type Color    
    void F() {      
        Color = Color.Black;            // References Color.Black static member                       
        Color = Color.Complement();     // Invokes Complement() on Color field  
    }   
    static void G() {       
    Color c = Color.White;          // References Color.White static member 
    } 
}

这里是关键部分:

E 的两种可能含义都是允许的。 E.I 的两种可能含义从不模棱两可,因为在这两种情况下,我都必须是类型 E 的成员。换句话说,该规则只允许访问 E 的静态成员和嵌套类型,否则会发生编译时错误。

当您定义Color Color = Color.Brown 时,情况会发生变化。由于 I(棕色)必须在两种情况下(静态和非静态)都是 E(颜色)的成员,因此此规则允许您访问两者,而不是由于当前(非-静态)上下文。但是,现在您已将其中一个上下文(您的非静态上下文)设为常量。由于它允许两者,它试图将Color.Brown 定义为枚举和类成员both,但它存在问题,具体取决于它自己的值(例如,您不能拥有const I = I + 1 )。

【讨论】:

  • 我投了赞成票(主要是因为您找到了 Connect 报告(即使它也在您对我的问题的评论中,也可以从这个答案链接它))。但是您的答案如何解释为什么非常量字段没有错误(例如问题中的C2.Color)而const 类型成员(C1.Color)出现错误?
  • 我将添加此答案的链接。如果我理解正确,编译器在成员名称与类型名称冲突时抛出错误,但它被故意忽略,因为两者都保证具有相同的字段('I's)。编译器仍然无法区分枚举和成员之间的区别,它只是没有告诉您它不能,只是使用了这两个定义。一旦将其定义为 const,它仍会尝试同时使用两者,并且成员定义使其成为循环引用。
【解决方案3】:

我确信这与常量的值必须在编译时确定,但(静态)属性的值将在运行时确定这一事实有关。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-13
    • 2021-02-15
    相关资源
    最近更新 更多