Как я могу сделать что-то вроде динамического перечисления в C #? - PullRequest
2 голосов
/ 07 августа 2009

В приложении, которое я создаю, у меня было перечисление статусов аккаунта:

 public enum AccountStatus
 {
      Active = 1,
      Trial = 2,
      Canceled = 3
 }

Однако мне нужно было больше информации из AccountStatus, поэтому я создал класс, который имеет несколько дополнительных полезных свойств:

 public class AccountStatus
 {
      public int Id {get; set;}
      public string Description {get; set;}
      public bool IsActive {get; set;}
      public bool CanReactivate {get; set;}
 }

Этот класс заполняется из таблицы базы данных, которая может выглядеть следующим образом:

 1,  "Active",       True,  True
 2,  "Trial",        True,  True 
 3,  "ExpiredTrial", False, True
 4,  "Expelled",     False, False

Это очень удобно, когда у меня есть объект customer, который использует AccountStatus, потому что я могу написать код вроде:

 if(customer.Status.CanReactivate) // Show reactivation form

Однако я потерял нечто не менее важное. Я больше не могу сделать это:

 if(customer.Status == AccountStatus.Active)  // allow some stuff to happen

Что было бы лучшим способом, если это вообще возможно, включить что-то, что позволит мне подражать перечислению в классе. Я знаю, что могу добавить открытые статические поля в класс AccountStatus, но в конечном итоге это не сработает, потому что, если база данных изменится, код придется обновить вручную. Под этим я подразумеваю:

 public static readonly AccountStatus Active = new AccountStatus(1);
 public static readonly AccountStatus Trial = new AccountStatus(2);
 // etc, etc ...

Я предполагаю, что где-то есть образец этого, я просто не знаю, как это называется.

Есть идеи?

ПОЯСНЕНИЯ

Основываясь на ответах, мне нужно уточнить пару вещей.

Таблица выше является кратким примером. В моей фактической таблице есть много записей, у меня там 12 прямо сейчас. Плюс мы можем добавить еще или удалить некоторые из существующих. Это то, что я имел в виду под «динамическим» в заголовке моего вопроса.

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

 if(customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)

... ни Trial, ни ExpiredTrial не являются логическими значениями свойства. Я тоже не хочу их добавлять. Это создаст еще худший прецедент, чем тот, который я пытаюсь избежать (это означает, что мне придется добавлять новое свойство в класс каждый раз, когда я добавляю новую запись в таблицу).

UPDATE

Я выбрал ответ, который на самом деле не встречал, я искал, но предполагает, что я искал что-то ненужное. Подумав об этом, я согласен. Хотя добавление перечислимых или статических полей дублирует некоторую работу (т. Е. Наличие значений как в коде, так и в таблице), я думаю, что преимущества перевешивают негативы.

Ответы [ 10 ]

4 голосов
/ 07 августа 2009

Но почему вы не можете использовать перечисление как свойство этого класса? ..

public enum State
{
    Active = 1,
    Trial = 2,
    Canceled = 3
}

public class AccountStatus
{
    public int Id {get; set;}
    public State State {get; set;}
    public string Description {get; set;}
    public bool IsActive {get; set;}
    public bool CanReactivate {get; set;}
}

А потом:

if(customer.Status == AccountStatus.State.Active)  // allow some stuff to happen
2 голосов
/ 07 августа 2009

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

public static readonly AccountStatus Active = new AccountStatus("Active");

или загрузите тип из вашей базы данных:

public static readonly AccountStatus Trial = new AccountStatus( reader["StatusField"] );

Затем вы можете сделать явные сравнения:

if(customer.Status == "Active")

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

редактировать

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

1 голос
/ 07 августа 2009

Я все еще думаю, что вам лучше всего добавить свои пропущенные дела в класс.

 public class AccountStatus
 {
      public int Id {get; set;}
      public string Description {get; set;}
      public bool IsActive {get; set;}
      public bool CanReactivate {get; set;}
      public bool Trial {get; set;}
      public bool ExpiredTrial {get; set;}
 }

Который вы можете назвать в более простой форме, чем ваш пример:

if(customer.AccountStatus.Trial || customer.AccountStatus.ExpiredTrial)

Если вам нужно проверить статус UserDefined, представьте его как отдельное свойство:

public AccountStatusCode Status  {get; set;}

... и назовите это так:

if(customer.Status == AccountStatus.Active)

Вы все еще можете добавить конструктор, если хотите установить начальный статус.

1 голос
/ 07 августа 2009

, если вы делаете / хотите что-то подобное в вашем приложении:

if (customer.Status == AccountStatus.Active)

Вы должны знать в своем коде, что «Активный» является возможным статусом. Как еще вы могли бы написать фактическое слово Active в вашем коде. Объект состояния может быть динамическим, но остальная часть программы, которая использует статус, должна знать, какие типы статуса существуют, чтобы сделать что-то полезное с ним. Что, если active больше не существует, объект status может не нуждаться в переопределении, но код, который его использует, делает.

Если статус каждого вида полностью определяется параметрами, которые кажутся почти такими (кажется, что параметры active и trail имеют одинаковые параметры, поэтому для их дифференциации требуется больше данных), проверьте эти параметры.

Если комбинация параметров должна иметь имя, то создайте какой-нибудь вид поиска, где вы можете перевести имя в связанные с ним параметры или его обратное. Таким образом, имя может быть динамическим для этого фрагмента кода, а значения параметров до некоторой степени.

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

1 голос
/ 07 августа 2009

Я не понимаю, почему ты не можешь просто написать:

if (customer.Status.IsActive)
1 голос
/ 07 августа 2009

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

[Flags]
public enum AccountStatus
{
    Expelled = 1,
    Active = 2,
    CanReactivate = 4,
    Canceled = 8,
    Trial = Active | CanReactivate,
    ExpiredTrial = CanReactivate,        
}

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

0 голосов
/ 18 января 2010

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

if (customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)

если бы я мог привести их в порядок, я мог бы сделать:

if (customer.Status

0 голосов
/ 07 августа 2009

ОК, хорошо, если вы работаете в C # 3.0, вы можете попробовать методы расширения :

// Define extension method like this:
public static bool IsActive(this AccountStatus status)      
{            
    get { return status == AccountStatusCode.Active; }      
}
// 
// Call it like this:
if (AccountStatus.IsActive())

Это исключает его из вашего класса.

0 голосов
/ 07 августа 2009

Этот код делает то, что вы описали в своем посте. Я не кодировал CanReactivate, потому что вы не сказали, какова логика для этого.

 public enum AccountStatusCode
 {
      Active = 1,
      Trial = 2,
      Canceled = 3
 }

 public class AccountStatus
 {
      private AccountStatusEnum status
      //
      // Constructor sets initial status.
      public AccountStatus(int status)
      {
          this.Status = (AccountStatusCode)status;
      }

      public int Id { get; set; }
      public string Description { get; set; }
      //
      // 
      public bool IsActive 
      { 
           get { return status == AccountStatusCode.Active; }
      }
      public bool CanReactivate { get; set; }
 }

Обратите внимание, что, поскольку вы сказали, что хотите указать начальный статус учетной записи как int, я принимаю int в конструкторе, но затем я приведу его к AccountStatusEnum для назначения его переменной-члену. Это, вероятно, не лучшая практика ... Вы должны передать конструктору значение AccountStatusCode.

0 голосов
/ 07 августа 2009

Вы можете установить связь «многие ко многим» между AccountStatus и Description. Таким образом, вы можете во время выполнения загрузить все полученные различные описания, а затем сравнить с ними, используя какое-то перечисление:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...