38

好的,所以 C# 语言规范有一个关于成员及其类型具有相同名称的规则的特殊部分(旧版本链接) 。Color Color著名的大师 Eric Lippert曾经在博客上写过它。

我在这里要问的问题在某种意义上(不)与在线程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)
        }
    }
4

3 回答 3

28

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

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

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

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

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

于 2015-03-02T22:46:13.347 回答
13

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

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

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

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

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

7.6.4.1 相同的简单名称和类型名称 在 EI 形式的成员访问中,如果 E 是单个标识符,并且如果 E 作为简单名称(第 7.6.2 节)的含义是常量、字段、属性,局部变量,或具有与 E 的含义相同类型的参数作为类型名称(第 3.8 节),则允许 E 的两种可能含义。EI 的两种可能含义从不模棱两可,因为在这两种情况下我都必须是类型 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 的两种可能含义都是允许的。EI 的两种可能含义从不模棱两可,因为在这两种情况下我都必须是类型 E 的成员。换句话说,该规则只允许访问 E 的静态成员和嵌套类型,否则会发生编译时错误。

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

于 2015-03-02T21:10:36.823 回答
0

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

于 2015-03-02T18:13:14.970 回答