В приложении, которое я создаю, я имел перечисление статусов учетной записи:
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
Это действительно удобно, когда у меня есть объект клиента, который использует 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
Я выбрал ответ, который действительно не встречался, я искал, но предполагает, что я искал что-то ненужное. Подумав об этом, я согласен. При добавлении переполнения или статических полей дублирование некоторой работы (т.е. Наличие значений как в коде, так и в таблице), я думаю, что преимущества перевешивают негативы.
Но почему вы не можете использовать перечисление как свойство этого класса..?
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
Вместо того, чтобы работать с сильно типизированным enum
, вы могли бы просто выполнять сравнения с помощью строки:
public static readonly AccountStatus Active = new AccountStatus("Active");
или загрузите тип из своей базы данных:
public static readonly AccountStatus Trial = new AccountStatus( reader["StatusField"] );
Затем вы можете делать явные сравнения:
if(customer.Status == "Active")
Вы теряете сильную типизацию, но то, что означает динамика:-). Вы можете сохранить известные значения строк в константах, чтобы вернуть их обратно.
изменить
Вы могли бы, конечно, сделать это, используя соответствующие значения целого числа, как вы намекали в конце своего сообщения. Но строки легче читать, и в этом случае использование целых чисел не дает каких-либо типизирующих преимуществ.
Я по-прежнему считаю, что лучше всего добавить свои недостающие случаи в класс.
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)
Вы можете добавить к нему конструктор, если хотите установить начальный статус.
Я думаю, вы могли бы добиться этого, используя перечисление Flags, где вы можете комбинировать значения:
[Flags]
public enum AccountStatus
{
Expelled = 1,
Active = 2,
CanReactivate = 4,
Canceled = 8,
Trial = Active | CanReactivate,
ExpiredTrial = CanReactivate,
}
Однако, кажется, что эти разные значения перечисления перемещаются в разных масштабах (некоторые описывают состояние, некоторые описывают действительные действия), поэтому это может быть неправильное решение. Возможно, вам следует разделить его на две части.
если вы делаете/хотите что-то подобное в своем приложении:
if (customer.Status == AccountStatus.Active)
Вы должны знать в своем коде, что "Активный" - это возможный статус. Как еще вы могли бы написать фактическое слово Active в своем коде. Объект состояния может быть динамическим, но остальная часть программы, использующая статус, должна знать, какие типы состояний существуют, чтобы сделать что-то полезное с ней. Что делать, если активный объект больше не существует, объект статуса может не нуждаться в повторном выполнении, но используемый код.
Если статус каждого типа полностью определен такими параметрами, как кажется почти (активные и трейлы имеют одинаковые параметры, поэтому для дифференциации (даты истечения срока действия?) требуется еще больше), затем проверьте эти параметры.
Если комбинация параметров должна иметь имя, тогда сделайте какую-то поисковую таблицу, в которой вы можете перевести имя в связанные с ним параметры или его инверсию. Таким образом, имя может быть динамическим для этой части кода, а значения параметров в некоторой степени.
Возможным реальным динамическим решением было бы реализовать какой-то язык сценариев /xml file/..., который позволяет пользователю указывать виды статуса, их параметры и связывать их с поведением системы.
Я не понимаю, почему вы не можете просто написать:
if (customer.Status.IsActive)
ExpiredTrial
? Если ExpiredTrial
специально определен как !IsActive && CanReactivate
- что, как кажется, в вашей таблице - тогда разве вы не должны просто проверить это?
Я думаю, что дзюдо попытался объяснить - новый статус в БД потребует поставить проверку s для этого нового статуса в условных блоках. Думаю, я тоже что-то делаю. Единственное, что я делаю, это то, что я также использую другое поле "rank", чтобы я мог выполнять сравнение диапазона вместо жесткого кодирования всех статусов. Например, вместо выполнения:
if (customer.Status == AccountStatus.Trial || customer.Status == AccountStatus.ExpiredTrial)
Если бы я мог привести их в порядок, я мог бы сделать:
if (customer.Status < AccountStatus.Trial), как в нашем перечислении, мы можем поместить их как упорядоченные. Таким образом, новый статус на новой странице не будет ломать логики других страниц (зависит от ранга статуса).
Этот код делает то, что вы описали в своем сообщении. Я не кодировал 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.
Хорошо, если вы работаете в С# 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())
Это избавит вас от вашего класса.
Вы можете создать взаимосвязь "многие ко многим" между AccountStatus и описанием. Таким образом, во время выполнения вы можете загружать все различные Описания, которые вы получили, а затем сравнивать с ними, используя какое-то перечисление:)