C# 编译器是否将 Color Color 规则错误地用于 const 类型成员?

Does the C# compiler get the Color Color rule wrong with const type members?

好的,所以 C# 语言规范有 a special section (old version linked) on the Color Color rule where a member and its type has the same name. Well-known guru 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 类型,也可以指代具有相同名称的 class 成员.但是上面说的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)
        }
    }

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

1) 它不适用于 const 因为它试图同时允许两种定义(枚举类型和 class 成员),所以它试图将自己定义为自身的一个功能。

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 Identical simple names and type names In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.6.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.8), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members and nested types of E where a compile-time error would otherwise have occurred. For example:

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 的两种可能含义绝不会含糊不清,因为在这两种情况下 I 都必须是类型 E 的成员。换句话说,该规则只允许访问 E 的静态成员和嵌套类型,否则会发生编译时错误。

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

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

就个人而言:我不记得在 2010 年首次报道时我是否负责调查这个问题,但由于当时我在圆度检测器上做了很多工作,所以可能性是非常好。

这远不是圆度检测器中存在的唯一错误;如果存在嵌套的泛型类型,而嵌套的泛型类型又具有其类型参数涉及嵌套类型的泛型基类型,它会变得非常混乱。

亚历克斯"won't fix"编了这个我一点也不惊讶;我花了很长时间重写执行 class 循环检测的代码,并且更改被认为风险太大。所有这些工作都交给了 Roslyn。

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