Вложенные Enum и конфликты именования свойств - PullRequest
28 голосов
/ 13 февраля 2010

Есть несколько связанных вопросов здесь и здесь , но они действительно не дали мне удовлетворительных ответов. Проблема в том, что перечисления, вложенные в класс в 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?

Я мог бы переместить их за пределы класса и изменить их на что-то вроде CardSuit и CardRank, но тогда я чувствовал бы, что я вставляю контекстную информацию в имя перечисления, когда это должно быть обработано имя класса или пространства имен.

Я мог бы изменить имена перечислений на Suits и Ranks, но это нарушает Рекомендации Microsoft по именованию . И это просто нехорошо.

Я мог бы изменить имена свойств. А к чему? Мне интуитивно кажется, что я хочу сказать Suit = Card.Suit.Spades.

Я мог бы переместить перечисления в отдельный статический класс с именем CardInfo, содержащий только эти перечисления. Если я не могу придумать что-то еще, я думаю, что это лучший вариант.

Так что мне интересно, что другие люди сделали в подобных ситуациях. Также было бы приятно узнать, почему это запрещено. Может быть, Эрик Липперт или кто-то может вмешаться в решение запретить это? Похоже, что это создает только неопределенность внутри класса, и это может быть решено путем принудительного использования this.Suit для имени свойства. (Похоже на устранение неоднозначности между местными жителями и членами.) Я предполагаю, что это было опущено из-за «каждая функция начинается с -100 баллов» , но мне было бы интересно обсудить это.

Ответы [ 7 ]

19 голосов
/ 13 февраля 2010

Было бы также приятно узнать, почему это запрещено. Может быть, Эрик Липперт или кто-то может вмешаться в решение запретить это?

Смысл правила в том, чтобы при поиске имени не было никакой двусмысленности в классе. Определенные области кода обозначаются как определяющие «пространство объявления». Фундаментальное правило пространств объявлений: никакие две вещи, объявленные в одном и том же пространстве объявлений, не имеют одинакового имени (за исключением методов, которые должны отличаться на подпись , а не имя .)

Создание исключений из этого правила только делает вещи более запутанными, а не менее запутанными. Я согласен, что досадно, что вы не можете иметь свойство и перечисление с тем же именем, объявленным в том же пространстве объявлений, но как только вы начнете делать исключения, это просто станет беспорядком. Обычно хорошим свойством является то, что имя однозначно идентифицирует группу методов, параметр типа, свойство и т. Д.

Обратите внимание, что это правило применяется к вещам , объявленным в пространстве объявлений, а не к вещам , использованным в пространстве объявлений. Совершенно законно говорить «общедоступный Suit Suit {get; set;}» при условии, что тип Suit не объявлен в том же пространстве объявления, что и свойство. Когда кто-то говорит «Suit.X», выяснить, относится ли X к типу (то есть X является статическим членом) или свойству (то есть X является элементом экземпляра), немного сложно. Подробности смотрите в моей статье о том, как мы это делаем:

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

3 голосов
/ 13 февраля 2010

Я бы согласился с переносом определения enum в другое место. В настоящее время перечисления видны только через карту, поэтому, если вы хотите проверить туз, вам нужно будет сделать

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

Где, если вы переместили его в отдельное определение, вы могли бы сделать это, если вы сделали его глобальным:

if (card.Suit == Suit.Ace) { } //no naming clashes, easier to read
3 голосов
/ 13 февраля 2010

Я предпочитаю называть enums, используя существительное, а затем Options. В вашем случае:

SuitOptions
RankOptions

В конце концов, enum - это просто набор возможных опций, верно?

Тогда у вас будет:

myCard.Suit = Card.SuitOptions.Clubs;

Что, на мой взгляд, имеет смысл, и вы по-прежнему можете узнать при просмотре текста, является ли тогда enum или свойство.

1 голос
/ 01 июля 2011

Интересно видеть, что хотя Microsoft Naming Guidelines говорит, что вы должны использовать единственное имя для большинства перечислений и множественные имена для битовых полей, пример кода для ключевого слова enum использует имя во множественном числе!

enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
1 голос
/ 13 февраля 2010

Ваши перечисления в основном являются типами данных, которые вы определили. Вы не будете использовать int или string в качестве имени члена, поэтому я думаю, что одинаково плохая идея использовать имена enum и имена членов в вашем случае.

1 голос
/ 13 февраля 2010

Я бы переместил перечисления за пределы определения класса и использовал бы пространство имен, чтобы указать, что они относятся к карточкам. Я вообще не люблю перечисления, вложенные в классы. Вы правы во множественном числе: единственное число для обычных перечислений, множественное число для флагов перечислений.

0 голосов
/ 12 марта 2018

Упаковка enum в struct хорошо подходила для моего конкретного случая, т.к. в игре были и другие данные (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;                
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...