Соглашение об именах C # для enum и сопоставления свойств

93

Я часто реализую класс, поддерживающий какое-то собственное свойство статуса в виде перечисления: у меня есть перечисление Status и ОДНО свойство Status типа Status. Как мне решить этот конфликт имен?

public class Car
{
  public enum Status
  {
    Off,
    Starting,
    Moving
  };

  Status status = Status.Off;

  public Status Status // <===== Won't compile =====
  {
    get { return status; }
    set { status = value; DoSomething(); }
  }
}

Если бы перечисление Status было общим для разных типов, я бы вынес его за пределы класса, и проблема была бы решена. Но Status применяется только к Car, поэтому нет смысла объявлять перечисление вне класса.

Какое соглашение об именах вы используете в этом случае?

NB: Этот вопрос частично обсуждался в комментариях к ответу на этот вопрос . Поскольку это не было главным вопрос, он не получил особого внимания.

РЕДАКТИРОВАТЬ: Филип Экберг предлагает отличный обходной путь IMO для конкретного случая «Статус». Тем не менее , я бы интересно читать о решениях , где имя перечислений / собственность отличается, как и в Майкла Prewecki в ответ .

EDIT2 (май 2010 г.): Мое любимое решение - использовать множественное число для имени типа перечисления, как было предложено Крисом С. Согласно рекомендациям MS, это следует использовать только для перечислений флагов. Но мне это нравится все больше и больше. Теперь я использую его и для обычных перечислений.

Серж Вотье
источник
1
Я не думаю, что для этого есть много хороших решений, если вы хотите, чтобы перечисление было вложено в ваш класс. На самом деле я предпочитаю, чтобы перечисление было отдельным, тогда это не представляет проблемы, но я могу философски понять вашу точку зрения о желании вложить его.
Крейг Ширер,

Ответы:

32

Я добавлю к обсуждению свой 1 евро, но это, вероятно, не добавит ничего нового.

Очевидное решение - переместить Status из вложенного Enum. Большинство перечислений .NET (за исключением, возможно, некоторых в пространстве имен Windows.Forms) не вложены, и разработчику, использующему ваш API, неудобно использовать префикс имени класса.

Одна вещь, о которой не упоминалось, - это то, что перечисления флагов в соответствии с рекомендациями MSDN должны быть существительными во множественном числе, которые вы, вероятно, уже знаете (Статус - это простое перечисление, поэтому следует использовать существительные в единственном числе).

State (перечисление States) - это звательный падеж, «Status» - именительный падеж существительного, которое английский язык, как и большая часть нашего языка, заимствовал из латыни. Звительный падеж - это то, что вы называете существительным в зависимости от его состояния, а именительный падеж - это подлежащее глагола.

Другими словами, когда машина движется , это глагол - движение - это ее статус. Но машина не трогается, а двигатель. И он не запускается, двигатель запускается (вы, вероятно, выбрали здесь пример, так что это может быть неуместно).

public class Car
{
  VehicleState _vehicleState= VehicleState.Stationary;

  public VehicleState VehicleState 
  {
    get { return _vehicleState; }
    set { _vehicleState = value; DoSomething(); }
  }
}

public enum VehicleState
{
    Stationary, Idle, Moving
}

Состояние - такое обобщенное существительное, не лучше ли описать, к какому состоянию оно относится? Как я сделал выше

Пример типа также, на мой взгляд, относится не к типу читателя, а к его базе данных. Я бы предпочел, чтобы вы описывали продукт базы данных читателя, который не обязательно имеет отношение к типу читателя (например, тип читателя может быть только прямым, кешированным и т. Д.). Так

reader.Database = Databases.Oracle;

На самом деле этого никогда не происходит, поскольку они реализованы в виде драйверов и цепочки наследования вместо использования перечислений, поэтому строка выше выглядит неестественно.

Крис С
источник
24
Насколько я понимаю, флаги должны иметь множественное число, а не простые перечисления.
Серж Вотье
8
Что касается перемещения перечислений из класса, мне еще предстоит понять, почему CarState удобнее, чем Car.State. Однако я не понимаю положительных сторон получения перечисления из класса, когда это перечисление описывает поведение только этого класса.
Серж Вотье
Мне следовало писать именные фразы, такие как FileOptions, а не только существительные, я обновил ответ. Я полагаю, что classname.Enum - это просто предпочтение - я не могу найти в структуре примеров, которые я копирую.
Chris S
Несмотря на то, что MS утверждает, что для флагов следует использовать множественное число, со временем эти решения мне нравились все больше и больше. Теперь я использую его и для перечислений. Поэтому я передумал и принял ваш ответ вместо ответа Филипа.
Серж Вотье
1
Я знаю, что опоздал на полтора года, но public class EngineStateдолжен быть public enum EngineStateв этом примере, верно?
Дэвид Мердок,
36

Определение «Выкл.», «Запуск» и «Движение» - это то, что я бы назвал «Состояние». И когда вы намекаете, что используете «Состояние», это ваш «Статус». Так!

public class Car
{
  public enum State
  {
    Off,
    Starting,
    Moving
  };

  State state = State.Off;

  public State Status
  {
    get { return state ; }
    set { state= value; DoSomething(); }
  }
}

Если мы возьмем другой пример из того, где вы хотели бы использовать слово «Тип», например, в этом случае:

public class DataReader
{
    public enum Type
    {
        Sql,
        Oracle,
        OleDb
    }

    public Type Type { get; set; } // <===== Won't compile =====

}

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

Когда что-то установлено в состояние, это определяется как статус "вещи".

Пример: Автомобиль находится в состоянии "Работа", "Остановлен" и т. Д.

Во втором примере вы хотите добиться примерно следующего:

myDataReader.Type = DataReader.Database.OleDb

Вы можете подумать, что это противоречит тому, о чем я проповедовал другим, что вам нужно следовать стандарту. Но вы следуете стандарту! Sql-case также является частным случаем и поэтому требует определенного решения.

Однако перечисление можно будет повторно использовать в вашем System.Dataпространстве, и в этом суть шаблонов.

Другой случай, который следует рассмотреть с «Типом», - это «Животное», где Тип определяет Вид.

public class Animal
    {
        public enum Type
        {
            Mammal,
            Reptile,
            JonSkeet
        }

        public Type Species{ get; set; }

    }

Это следует шаблону, вам не нужно специально «знать» для этого Object, и вы не указываете «AnimalType» или «DataReaderType», вы можете повторно использовать перечисления в выбранном вами пространстве имен.

Филип Экберг
источник
1
Я считаю, что название «Статус» было просто примером. А как насчет других ситуаций, когда вы не можете полагаться на статус / состояние? Например, перечисление типа ...
Дэн С.
2
@Filip: образец Animal Type точно указывает на то, что я имел в виду в первом комментарии, т.е. это в значительной степени "переформулировка" перечисления. Я думаю, что использование синонимов для перечисления и имени свойства несколько сбивает код.
Дэн С.
1
Я также думаю, что «вид» - это просто переформулировка типа (в данном случае для животных).
LegendLength
2
Простое переименование не всегда так чисто - подумайте о классе для игральной карты, например, с перечислением Suit и свойством Suit - переименовать в какой именно? В любом случае неуклюже.
annakata
1
такой подход ведет к недопониманию и сбивает с толку код
Boogier
9

Я думаю, что настоящая проблема здесь в том, что статус перечисления инкапсулирован внутри вашего класса, поэтому это Car.Statusнеоднозначно как для свойстваStatus и для перечисления.Status

А еще лучше вынести перечисление вне класса:

public enum Status
{
    Off,
    Starting,
    Moving
}

public class Car
{
    public Status Status
    { ... }
}

ОБНОВИТЬ

Благодаря комментариям ниже я объясню свой дизайн выше.

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

public class Car
{
    public enum Status
    {...}
    ...
    public Status CarStatus { get; set;}
}

Хотя некоторые комментаторы утверждают, что Status не имеет никакого значения за пределами класса Car, тот факт, что вы устанавливаете общедоступное свойство, означает, что есть другие части программы, которые будут использовать это перечисление:

public Car myCar = new Car();
myCar.CarStatus = Car.Status.Off;

И что для меня это код запах. Если я буду смотреть на этот статус внешней части Car, я мог бы также определить его пределами , а также.

Поэтому я, вероятно, просто переименую его как:

public enum CarStatus
{...}

public class Car
{
    ...
    public CarStatus Status { get; set; }
}

Однако, если это перечисление будет использоваться внутри и только внутри автомобильного класса, я вполне могу объявить перечисление там.

Джон Лимджап
источник
Это сделает статус глобальным, вы можете захотеть иметь его только внутри определенной области. «Статус» для других типов, таких как «Человек», может не иметь «Запуск». Но вы должны следовать шаблону.
Филип Экберг,
Нет, он на высоте. В 99% случаев перечисление - это то же слово, которое вы хотите использовать для свойства. Перечисление - это просто именованный флаг, поэтому он не причиняет большого вреда при жизни за пределами (в отличие от классов, содержащих логику)
Quibblesome
Джон, это именно моя точка зрения: перечисление Status имеет значение только в отношении Car. Совершенно разные типы объектов будут иметь совершенно разные статусы,
Серж Вотье
они будут в разных пространствах имен, так что это не имеет значения
Chris S
Объяснение, что желание взглянуть на это за пределами автомобиля имеет смысл. Но в моем случае у меня есть два класса, ORи CR. Оба имеют свой собственный набор состояний, поэтому нельзя определять их вне их собственной области.
deed02392
4

Я знаю, что мое предложение идет вразрез с соглашениями об именах .NET, но я лично префикс перечислений с помощью «E» и флаги перечисления с помощью «F» (аналогично тому, как мы префикс интерфейсов с помощью «I»). Я действительно не понимаю, почему это не конвенция. Перечисления / флаги - это особый случай, подобный интерфейсам, которые никогда не меняют свой тип. Он не только проясняет, что это такое, но и очень легко вводить intellisense, поскольку префикс будет фильтровать большинство других типов / переменных / и т. Д., И у вас не будет этих конфликтов имен.

И это также решит другую проблему, когда для примеров в WPF они используют статические классы, такие как перечисления (например, FontWeights), которые имеют предварительно определенные экземпляры типов, но вы не узнаете, если не будете искать его. Если бы они просто поставили перед ними префикс «E», все, что вам нужно было бы сделать, это ввести символ, чтобы найти эти специальные статические классы.


источник
4

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

public class Car
{
  public enum StatusEnum
  {
    Off,
    Starting,
    Moving
  };

  public StatusEnum Status { get; set; }

}
Натанчер
источник
9
Однако имейте в виду, что в соглашениях Microsoft об именовании перечислений говорится: X НЕ используйте суффикс «Enum» в именах типов перечислений.
DavidRR
2
Потому что условности Microsoft проверены временем.
Натанчер
2

Я бы изменил имя свойства на что-то вроде CurrentStatus. Быстро и легко :)

TWith2Sugars
источник
1

Я предлагаю добавить «Option» к имени типа (или Flag, если он содержит битовые флаги), т.е. тип - Car.StatusOption, а свойство - Car.Status.

По сравнению с множественным числом, это позволяет избежать конфликтов имен при создании коллекций типа enum, где вы обычно хотите использовать множественное число для свойства коллекции , а не для типа enum .

Кристиан Ведберг
источник
0

Обычно я использую префикс для перечислений, например CarStatus. Я полагаю, все зависит от команды, с которой вы работаете (есть ли у них какие-либо правила / процессы для такого рода вещей) и использования объектов. Только мои 2 цента (:

Кирон
источник
1
Это фактически уничтожило бы соглашение об именах с MyObjectName перед Status.
Филип Экберг,
Пространства имён из класса должно быть достаточно, чтобы справиться с этим в любом случае
annakata