33

There are some related questions here and here, but they didn't really give me satisfactory answers. The problem is that enums nested in a class in C# cannot have the same name as a property of the class. My example:

public class Card
{
    public enum Suit
    {
        Clubs,
        Diamonds,
        Spades,
        Hearts
    }

    public enum Rank
    {
        Two,
        Three,
        ...
        King,
        Ace
    }

    public Suit Suit { get; private set; }
    public Rank Rank { get; private set; }
    ...
}

There are a few options to hack around this, but they don't seem right to me.

I could move the enums outside the class, but then you would just say Suit instead of Card.Suit, which seems wrong to me. What is a Suit outside the context of a Card?

I could move them outside the class and change them to something like CardSuit and CardRank, but then I'd feel like I'm baking context information into the name of the enum, when that should be handled by a class or namespace name.

I could change the names of the enums to Suits and Ranks, but this violates Microsoft's naming guidelines. And it just doesn't feel right.

I could change the property names. But to what? It feels intuitively right to me to want to say Suit = Card.Suit.Spades.

I could move the enums into a separate static class called CardInfo containing only these enums. If I can't come up with anything else, I think this is the best option.

So I'm wondering what other people have done in similar situations. It would also be nice to know why this is disallowed. Maybe Eric Lippert or someone could chime in on the decision to forbid it? It seems like it only creates ambiguity within the class, and this could be resolved by forcing the use of this.Suit for the property name. (Similar to disambiguating between locals and members.) I assume this was left out due to the "every feature starts with -100 points" thing, but I would be curious about discussions around this.

Community
  • 1
  • 1
Sean Devlin
  • 1,662
  • 12
  • 17
  • 1
    Thanks for the feedback, everyone. I'm going with `CardInfo`, but I don't think there's one canonical answer here. There are a lot of decent options, and I think it comes down to style. – Sean Devlin Feb 13 '10 at 16:19
  • 1
    Nesting `enum` is ok if you are going to use them only (mostly?) inside the class. Then you have to name `enum` *slightly* differently (adding *Type*, *Mode*, *Option*, etc to the name) to avoid name conflict with field/property. If there will be many users of that `enum` outside of the class, then nesting is a bad idea. You can declare it in same cs-file (to have at least some sort of relations with class where it is used). Nesting is undesirable in wpf too (not necessarily `enum`, but in general). – Sinatr Dec 09 '14 at 10:53

7 Answers7

19

It would also be nice to know why this is disallowed. Maybe Eric Lippert or someone could chime in on the decision to forbid it?

The point of the rule is to ensure that there is no ambiguity within the class when looking up a name. Certain regions of code are designated as defining a 'declaration space'. The fundamental rule of declaration spaces is no two things declared in the same declaration space have the same name (except for methods, which must differ by signature, not name.)

Making exceptions to this rule just makes things more confusing, not less confusing. I agree that it is vexing that you cannot have a property and an enum of the same name declared in the same declaration space, but once you start making exceptions then it just gets to be a mess. It's usually a nice property that a name uniquely identifies a method group, type parameter, property, and so on.

Note that this rule applies to things declared in a declaration space, not things used in a declaration space. It is perfectly legal to say "public Suit Suit { get; set; }" provided that the type Suit is not declared in the same declaration space as the property. When someone says "Suit.X", figuring out whether X is on the type (that is, X is a static member) or the property (that is, X is an instance member) is a bit tricky. See my article on how we do that for details:

http://blogs.msdn.com/ericlippert/archive/2009/07/06/color-color.aspx

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Is there any sort of standard naming convention for dealing with this problem? In my particular case, I'm trying to create a nested class structure for holding the values of a hierarchical (json) settings file. My natural tendency would be to name the properties the same as the nested class (e.g., if I have a nested class `Settings.Color`, I'd want to have `public Color Color { get; set; }`), but of course this won't compile. My options seem to be either to rename the class or the property, say, `ColorInfo`, but this could lead to ugly names for items deep in the hierarchy. – devuxer May 09 '16 at 18:37
  • @devuxer: I don't know of a good way to solve that problem, but usually it is not that much of a concern because it is considered a bad smell to make a public nested type. If the nested type is not public then you never have a public property that exposes the type like that. I would avoid nested types; I only use them when the nested type is a private implementation detail of the outer type. – Eric Lippert May 09 '16 at 21:21
  • Ah, that makes sense, thanks. So I could just "un-nest" my nested types and make them stand-alone public classes. This answer of yours helps as well: http://stackoverflow.com/a/7984625/129164. – devuxer May 09 '16 at 21:47
5

I prefer to name enums using a noun followed by Options. In your case:

SuitOptions
RankOptions

After all, the enum is just a set of possible options, right?

You will have then:

myCard.Suit = Card.SuitOptions.Clubs;

Which in my opinion makes sense and you are still able to know when viewing the text whether is then enum or property.

Victor Hurdugaci
  • 28,177
  • 5
  • 87
  • 103
  • 4
    Since an enum is a set of options by definition, it seems redundant to bake that into the name. It seems equivalent to adding an -Enum suffix, which the style guidelines say not to do. – Sean Devlin Feb 13 '10 at 15:40
  • 1
    I agree that adding -Enum suffix is redundant but I still believe that -Options is a good choice because: it describes what that type is while just "Suits" will make me think to a collection of suits and simple "Suit" is not working in this context. – Victor Hurdugaci Feb 13 '10 at 15:44
  • 2
    Are there any enums where you think -Options is an inappropriate suffix? I can't think of any where it *wouldn't* fit, which I think is the problem - it's too general. It's like -Manager or -Helper on a class. – Sean Devlin Feb 13 '10 at 15:47
  • 5
    One note, use plural SuitOptions only if it is a flag enum, regular enums should be singular SuitOption as per the people who developed the .NET framework(although they admit messing up and releasing things that violated that sometimes so don't be surprised if you find a counter-example). I do the same sometimes, but prefer the suffix "Type"(but I've considered "Code" as well). In DB world I encounter the same problem frequently since I often have things like both a large Roles table and then a RoleTypes code table(which often is mapped respectively to a class and enum in C#). – AaronLS Dec 20 '12 at 02:38
3

I would agree with moving the enum definition to a separate place. Currently, the enums are only visible through card, so if you wanted to check for an ace, you would have to do

if (card.CardSuit == Card.Suit.Ace) { }  //need different name for Suit field

Where if you moved it to a separate definition, you could do this if you made it global:

if (card.Suit == Suit.Ace) { } //no naming clashes, easier to read
Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157
  • 1
    Nested types/enums should be avoided if they will be used often. For example, they are difficult to discover via the IDE/Intellisense because even when you type "Suit" you won't get the prompt to automatically add the `using`, since you must access the enum as `Card.Suit` as in Crazy's exmaple. Nest a type only if it is either used only internally within the class, is needed externally very rarely, or is only used in advanced scenarios such as inheriting the class. – AaronLS Dec 20 '12 at 02:44
3

It't interesting to see that while the Microsoft Naming Guidelines say you should use a singular name for most enums and plural names for bit fields, the code sample for the enum keyword does use a plural name!

enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
comecme
  • 6,086
  • 10
  • 39
  • 67
1

I would move the enumerations outside the class definition and use the namespace to indicate that they relate to cards. I generally don't like enums nested inside classes. You're right about the plurality, though: singular for regular enums, plural for flags enums.

Kevin Kibler
  • 13,357
  • 8
  • 38
  • 61
  • 1
    That's another option, but you quickly run into similar problems. What should the namespace be called? `Card` is out, since then it would conflict with the class name. You could nest it another level down in an `Enums` namespace, I guess. Something like `Core.Enums.Card` where the class itself is `Core.Card`. That's not a bad option. – Sean Devlin Feb 13 '10 at 15:45
1

Wrapping the enum in a struct was well suited for my particular case b/c there was other data at play (the int Value, which is also a value type).

public class Record
{
    public enum DurationUnit { Minutes, Hours }

    public struct DurationStruct
    {
        public readonly int Value;
        public readonly DurationUnit Unit;

        public DurationStruct(int value, DurationUnit unit)
        {
            Value = value;
            Unit = unit;
        }
    }
    public DurationStruct Duration; //{get; set;} -can't return a ref to a val type (in C#)

    public void Init()
    {   
        // initialize struct (not really "new" like a ref type)
        // -helpful syntax if DurationStruct ever graduated/ascended to class
        Duration = new DurationStruct(1, DurationUnit.Hours);
    }
}    

So for the Card class above, it would be something like the following

public class Card
{
    public enum Suit { Clubs, Diamonds, Spades, Hearts }
    public enum Rank { Two, Three, ...  King, Ace }

    public struct CardStruct
    {
        public Card.Suit Suit { get; private set; }
        public Card.Rank Rank { get; private set; }
    }

    //public CardStruct Card {get; set;} -can't be same as enclosing class either
    public CardStruct Identity {get; set;}

    public int Value
    {
        get    
        {
            //switch((Suit)Card.Suit)
            switch((Suit)Identity.Suit)
            {
                //case Suit.Hearts:   return Card.Rank + 0*14;
                case Suit.Hearts:   return Identity.Rank + 0*14;
                case Suit.Clubs:    return Identity.Rank + 1*14;
                case Suit.Diamonds: return Identity.Rank + 2*14;
                case Suit.Spades:   return Identity.Rank + 3*14;                
            }
        }
    }
}
samus
  • 6,102
  • 6
  • 31
  • 69
0

Your enums are basically data types which you have defined. You would not use 'int' or 'string' as a member name, so I think it is an equally bad idea to use the your enum names and member names in your case.

Ray
  • 21,485
  • 5
  • 48
  • 64
  • 2
    So what would you name them in this *specific* case? `int` and `string` are very general things, but `Suit` and `Rank` are only meaningful in the context of a `Card`. – Sean Devlin Feb 13 '10 at 15:41
  • 1
    I really hate it when a perfectly good philosophical position gets tripped up by practical reality :) In my own naming scheme, I always use camelCase for my property names, so my property declaration would by "public Suit suit" which would be just fine. If you can't do that, then I like your CardInfo class idea. And CardSuit (as you suggested) or SuitOptions (from Victor) aren't bad either. Once in a while, I guess, nice, logical real-world naming of our objects doesn't mesh with what the compiler allows us to do. – Ray Feb 13 '10 at 16:02
  • 1
    True. There are a few decent options in the answers here, but none feels *ideal*. I'm leaning towards my `CardInfo` thing, but I would, wouldn't I? I suggested it, after all. I think this is largely a style choice. – Sean Devlin Feb 13 '10 at 16:06
  • Always nice to answer your own question, in life and in SO – Ray Feb 13 '10 at 16:09