嵌套枚举和属性命名冲突

这里和这里有一些相关的问题,但它们并没有真正给我满意的答案。 问题是嵌套在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 ,这对我来说似乎不对。 在Card背景之外什么是Suit

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

我可以将枚举的名称更改为SuitsRanks ,但这违反了Microsoft的命名准则 。 它感觉不对劲。

我可以更改属性名称。 但到了什么? 想要说出Suit = Card.Suit.Spades对我来说直觉是对的。

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

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

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

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

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

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

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

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

 SuitOptions RankOptions 

毕竟,枚举只是一组可能的选择,对吧?

你会有:

 myCard.Suit = Card.SuitOptions.Clubs; 

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

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

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

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

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

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

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

有趣的是,虽然Microsoft命名准则说你应该为大多数枚举使用单数名称和为位字段使用复数名称,但enum关键字的代码示例使用复数名称!

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

struct包装enum非常适合我的特定情况b / c还有其他数据正在运行( 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; } } } }