tags:

views:

154

answers:

5

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.

+1  A: 

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
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
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
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
+1  A: 

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.

CodeSavvyGeek
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
+1  A: 

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
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
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
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
Always nice to answer your own question, in life and in SO
Ray
+2  A: 

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
CrazyJugglerDrummer
+8  A: 

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
That makes sense to me. Thanks for the info!
Sean Devlin