【问题标题】:Nested Enum and Property Naming Conflicts嵌套枚举和属性命名冲突
【发布时间】:2011-01-16 12:19:36
【问题描述】:

有一些相关的问题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,这对我来说似乎是错误的。在Card 的上下文之外,Suit 是什么?

我可以将它们移到课堂外并将它们更改为 CardSuitCardRank 之类的东西,但是我觉得我正在将上下文信息烘焙到枚举的名称中,而这应该由类或命名空间名称。

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

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

我可以将枚举移动到一个名为CardInfo 的单独静态类中,该类只包含这些枚举。如果我想不出别的办法,我认为这是最好的选择。

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

【问题讨论】:

  • 感谢大家的反馈。我会选择CardInfo,但我认为这里没有一个规范的答案。有很多不错的选择,我认为这取决于风格。
  • 嵌套enum 是可以的,如果你打算只(大部分?)在课堂上使用它们。然后你必须以不同的方式命名enum稍微(在名称中添加TypeModeOption等) 以避免与字段/属性发生名称冲突。如果在课堂之外会有很多enum 的用户,那么嵌套是个坏主意。您可以在同一个 cs 文件中声明它(至少与使用它的类有某种关系)。 wpf 中也不需要嵌套(不一定是enum,但一般来说)。

标签: c# .net enums


【解决方案1】:

enum 包装在 struct 中非常适合我的特殊情况 b/c 还有其他数据在起作用(@987654323 @ 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;                
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    看到Microsoft Naming Guidelines 说您应该对大多数枚举使用单数名称,而对位字段使用复数名称,这并不有趣,the code sample for the enum keyword 确实使用复数名称!

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

    【讨论】:

      【解决方案3】:

      很高兴知道为什么不允许这样做。也许 Eric Lippert 或其他人可以插话禁止它的决定?

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

      对这条规则做出例外只会让事情变得更混乱,而不是更混乱。我同意你不能在同一个声明空间中声明一个属性和一个同名的枚举是令人烦恼的,但是一旦你开始制作异常,它就会变得一团糟。它通常是一个很好的属性,名称​​唯一 标识方法组、类型参数、属性等。

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

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

      【讨论】:

      • 这对我来说很有意义。感谢您的信息!
      • 是否有任何标准命名约定来处理这个问题?在我的特殊情况下,我正在尝试创建一个嵌套类结构来保存分层(json)设置文件的值。我的自然倾向是将属性命名为与嵌套类相同的名称(例如,如果我有一个嵌套类Settings.Color,我想要public Color Color { get; set; }),但这当然不会编译。我的选择似乎是重命名类或属性,例如ColorInfo,但这可能会导致层次结构深处的项目名称难看。
      • @devuxer:我不知道解决这个问题的好方法,但通常它不是那么重要,因为创建公共嵌套类型被认为是一种难闻的气味。如果嵌套类型不是公共的,那么您永远不会有一个公共属性来公开这种类型。我会避免嵌套类型;我只在嵌套类型是外部类型的私有实现细节时使用它们。
      • 啊,有道理,谢谢。所以我可以“取消嵌套”我的嵌套类型并使它们成为独立的公共类。你的这个回答也有帮助:stackoverflow.com/a/7984625/129164.
      【解决方案4】:

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

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

      如果你把它移到一个单独的定义中,你可以这样做,如果你把它变成全局的:

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

      【讨论】:

      • 如果经常使用嵌套类型/枚举,则应避免使用它们。例如,它们很难通过 IDE/Intellisense 发现,因为即使您键入“Suit”,您也不会收到自动添加 using 的提示,因为您必须以 Card.Suit 访问枚举,就像在 Crazy 的示例中一样.仅当类型仅在类内部使用、外部很少需要或仅在继承类等高级场景中使用时才嵌套类型。
      【解决方案5】:

      您的枚举基本上是您定义的数据类型。您不会使用 'int' 或 'string' 作为成员名称,因此我认为在您的情况下使用枚举名称和成员名称同样是个坏主意。

      【讨论】:

      • 那么在这个特定的案例中你会给它们起什么名字呢? intstring 是非常笼统的东西,但 SuitRank 仅在 Card 的上下文中才有意义。
      • 我真的很讨厌一个完美的哲学立场被实际现实绊倒:) 在我自己的命名方案中,我总是使用驼峰命名法作为我的财产名称,所以我的财产声明会使用“public Suit”西装”,这很好。如果你不能这样做,那么我喜欢你的 CardInfo 类的想法。 CardSuit(如您所建议的)或 SuitOptions(来自 Victor)也不错。偶尔,我猜,我们对象的漂亮、合乎逻辑的真实世界命名与编译器允许我们做的事情不相符。
      • 是的。这里的答案有一些不错的选择,但没有一个感觉理想。我倾向于我的CardInfo,但我愿意,不是吗?毕竟是我建议的。我认为这主要是一种风格选择。
      • 总是很高兴回答你自己的问题,在生活和 SO
      【解决方案6】:

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

      【讨论】:

      • 这是另一种选择,但您很快就会遇到类似的问题。命名空间应该叫什么? Card 不存在了,因为它会与类名冲突。我猜你可以将它嵌套在Enums 命名空间中的另一层。类似于Core.Enums.Card 的类本身就是Core.Card。这不是一个糟糕的选择。
      【解决方案7】:

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

      SuitOptions
      RankOptions
      

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

      你将拥有:

      myCard.Suit = Card.SuitOptions.Clubs;
      

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

      【讨论】:

      • 由于枚举是定义上的一组选项,因此将其添加到名称中似乎是多余的。这似乎等同于添加一个 -Enum 后缀,样式指南说不要这样做。
      • 我同意添加 -Enum 后缀是多余的,但我仍然相信 -Options 是一个不错的选择,因为:它描述了该类型是什么,而仅“Suits”会让我想到一套西装并且简单的“西装”在这种情况下不起作用。
      • 有没有你认为 -Options 是不合适的后缀的枚举?我想不出它不适合的任何地方,我认为这是问题所在——它太笼统了。就像类上的 -Manager 或 -Helper 一样。
      • 请注意,仅当它是标志枚举时才使用复数 SuitOptions,根据开发 .NET 框架的人,常规枚举应该是单数 SuitOption(尽管他们承认搞砸并发布了违反该规则的东西有时,如果您找到反例,请不要感到惊讶)。我有时也会这样做,但更喜欢后缀“Type”(但我也考虑过“Code”)。在 DB 世界中,我经常遇到同样的问题,因为我经常有大型 Roles 表和 RoleTypes 代码表(通常分别映射到 C# 中的类和枚举)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-04
      相关资源
      最近更新 更多