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

谁都会走 提交于 2020-07-16 17:41:14

问题


Okay, so the C# Language Specification has 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 about it.

The question I am going to ask here is in a sense (not) quite the same that was asked in the thread Circular definition in a constant enum. You can go and upvote that other question if you like.

Now for my question. Consider this code:

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
    }
}

The point here is that in each situation above, the right-most identifier Color can refer either to the enum type, or to the class member with the same name. But the Color Color rule mentioned above means that we should see if the member (Brown) is static or non-static. Since it is static in this case, we should interprete Color accordingly.

My obvious main question: Why does this not work with a const type member? Is this unintended?

(Obviously, saying N.Color.Brown (N is the namespace) "fixes" it; I am not asking about that!)


Side note: With local variable const, the above anomaly does not exist:

    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:


It is a bug. I am unable to reproduce the problem in CTP 5 of VS 2015, and I think this one should have been fixed as part of the Roslyn rewrite. However, a commenter below notes that they can reproduce it in CTP 6. So I'm not sure what is going on here as far as whether this bug has been fixed or not.

On a personal note: I do not specifically recall if I was tasked with looking into this one when it was first reported back in 2010 but since I did quite a bit of work on the circularity detectors around then, odds are pretty good.

This is far from the only bug there was in the circularity detectors; it would get quite confused if there were nested generic types which in turn had generic base types whose type arguments involved the nested types.

I am not at all surprised that Alex "won't fix"ed this one; I spent quite a long time rewriting the code that did class circularity detection and the change was deemed too risky. All that work was punted to Roslyn.

If you're interested to see how the Color Color binding code works in Roslyn, take a look at the aptly-named method BindLeftOfPotentialColorColorMemberAccess -- I love me some descriptive method names -- in Binder_Expressions.cs.




回答2:


1) It doesn't work with a const because it's trying to allow both definitions (enum type and class member) at the same time, and so it tries to define itself as a function on itself.

2) Is it unintended? sort of. It's an unintended consequence to an intended behavior.

Basically, this is a bug that Microsoft acknowledges but has filed as 'Won't Fix', documented on Connect here.

I can't find the 5.0 language spec online (in article or blog form) anywhere but if you're interested, you can download it here. We're interested in page 161, section 7.6.4, Member Access, and it's first section 7.6.4.1, which is the same section the OP linked to (it was 7.5.4.1 then).

The fact that you can name a member and a type the exact same name (e.g., Color) is something that was explicitly allowed, even though your identifier now has two separate meanings. Here is the spec's language:

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 
    } 
}

Here's the key part:

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.

When you define Color Color = Color.Brown, something changes. Since I (Brown) must be a member of E (Color) in both cases (static and not-static), this rule allows you access to both, instead of restricting one due to the current (non-static) context. However, now you've made one of the contexts (your non-static one) a constant. Since it's allowing both, it's trying to define Color.Brown as both the enum and the class member, but there's a problem with it depending on it's own value (you can't have const I = I + 1 for example).




回答3:


I am sure it has something to do with the fact that the value of the constant must be deterministic at compile time, but the value of the (static) property will be determined at run-time.



来源:https://stackoverflow.com/questions/28775034/does-the-c-sharp-compiler-get-the-color-color-rule-wrong-with-const-type-members

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!