嵌套枚举和属性命名冲突

时间:2010-02-13 15:26:35

标签: c# .net enums

有一些相关问题herehere,但他们并没有给我满意的答案。问题是嵌套在C#中的类的枚举不能与类的属性具有相同的名称。我的例子:

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

有一些选择可以解决这个问题,但它们对我来说似乎并不合适。

我可以在课堂外移动枚举,但是你只会说Suit而不是Card.Suit,这对我来说似乎不对。在Suit的上下文之外的Card是什么?

我可以将它们移到课堂外并将其更改为CardSuitCardRank之类的内容,但后来我觉得我正在将上下文信息烘焙到枚举的名称中,当时应该由类或命名空间名称处理。

我可以将枚举的名称更改为SuitsRanks,但这会违反Microsoft's naming guidelines。它只是感觉不对。

我可以更改属性名称。但到了什么?想要说Suit = Card.Suit.Spades,我觉得直觉上是正确的。

我可以将枚举移动到一个名为CardInfo的单独的静态类中,只包含这些枚举。如果我不能提出任何其他建议,我认为这是最好的选择。

所以我想知道其他人在类似情况下做了什么。知道为什么不允许这样做也很好。也许埃里克·利珀特(Eric Lippert)或其他人可能会决定禁止它?它似乎只会在类中产生歧义,这可以通过强制使用this.Suit来解析属性名称来解决。 (类似于消除本地人和成员之间的歧义。)我认为由于"every feature starts with -100 points"事情而遗漏了这一点,但我对此讨论感到好奇。

7 个答案:

答案 0 :(得分:19)

  

知道为什么不允许这样做也很好。也许埃里克·利珀特(Eric Lippert)或其他人可能会决定禁止它?

规则的要点是确保在查找名称时类中没有歧义。某些代码区域被指定为定义“声明空间”。声明空间的基本规则是在同一声明空间中声明的两个事物没有相同的名称(方法除外,方法必须不同签名,而不是 name 。)

对此规则进行例外处理会让事情变得更加混乱,而不会让人感到困惑。我同意你不能在同一个声明空间中声明一个属性和一个同名的枚举,但是一旦你开始制作异常,它就会变得一团糟。名称​​唯一标识方法组,类型参数,属性等通常是一个不错的属性。

请注意,此规则适用于声明空间中声明的内容,而不适用于声明空间中使用的内容。如果类型Suit未在与属性相同的声明空间中声明,则说“public Suit Suit {get; set;}”是完全合法的。当有人说“Suit.X”时,弄清楚X是否在类型上(即X是静态成员)或属性(即X是实例成员)有点棘手。有关详细信息,请参阅我的文章:

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

答案 1 :(得分:3)

我更喜欢使用名词后跟选项来命名枚举。 在你的情况下:

SuitOptions
RankOptions

毕竟,枚举只是一组可能的选项,对吗?

您将拥有:

myCard.Suit = Card.SuitOptions.Clubs;

在我看来这是有道理的,你在查看文本时仍然能够知道当时是枚举还是属性。

答案 2 :(得分:3)

我同意将枚举定义移到另一个地方。目前,枚举只能通过卡片显示,所以如果你想检查一张王牌,你就必须这样做

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

如果您将其移动到单独的定义,那么如果您将其设置为全局,则可以执行此操作:

if (card.Suit == Suit.Ace) { } //no naming clashes, easier to read

答案 3 :(得分:1)

我会在类定义之外移动枚举,并使用命名空间来指示它们与卡有关。我一般不喜欢嵌套在类中的枚举。不过你对数字是正确的:常规枚举的单数,标志枚举的复数。

答案 4 :(得分:1)

您的枚举基本上是您定义的数据类型。你不会使用'int'或'string'作为成员名称,所以我认为在你的案例中使用你的枚举名和成员名是一个同样糟糕的主意。

答案 5 :(得分:1)

有趣的是,虽然Microsoft Naming Guidelines说你应该为大多数枚举使用单数名称而对位字段使用复数名称,the code sample for the enum keyword 确实使用复数名字!

enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };

答案 6 :(得分:0)

enum 包装在 struct 中非常适合我的特定情况b / c还有其他数据正在发挥作用(< strong> int Value,也是值类型)。

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

因此,对于上面的Card类,它将类似于以下

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