Поведение для имитации перечисления, реализующего интерфейс
Скажем, у меня есть перечисление что-то вроде:
enum OrderStatus
{
AwaitingAuthorization,
InProduction,
AwaitingDespatch
}
Я также создал метод расширения для моего перечисления, чтобы убрать отображаемые значения в пользовательском интерфейсе, поэтому у меня есть что-то вроде:
public static string ToDisplayString(this OrderStatus status)
{
switch (status)
{
case Status.AwaitingAuthorization:
return "Awaiting Authorization";
case Status.InProduction:
return "Item in Production";
... etc
}
}
Вдохновленный отличным сообщением здесь, я хочу привязать свои перечисления к SelectList
с помощью метода расширения:
public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
однако, чтобы использовать значения DisplayString в выпадающем меню, мне нужно добавить ограничение вдоль строк
: where TEnum has extension ToDisplayString
Очевидно, что ничто из этого не будет работать вообще с нынешним подходом, если только не найдется какой-то хитроумный трюк, о котором я не знаю.
Есть ли у кого-нибудь идеи о том, как я могу реализовать что-то вроде этого?
Ответы
Ответ 1
Есть ли веская причина использовать enum
здесь?
Когда вы начинаете прыгать через сумасшедшие обручи, чтобы использовать enum
s, возможно, пришло время использовать класс.
public class OrderStatus
{
OrderStatus(string display) { this.display = display; }
string display;
public override string ToString(){ return display; }
public static readonly OrderStatus AwaitingAuthorization
= new OrderStatus("Awaiting Authorization");
public static readonly OrderStatus InProduction
= new OrderStatus("Item in Production");
public static readonly OrderStatus AwaitingDispatch
= new OrderStatus("Awaiting Dispatch");
}
Вы потребляете его так же, как enum
:
public void AuthorizeAndSendToProduction(Order order, ProductionQueue queue)
{
if(order.Status != OrderStatus.AwaitingAuthorization)
{
Console.WriteLine("This order is not awaiting authorization!");
return;
}
order.Status = OrderStatus.InProduction;
queue.Enqueue(order);
}
Строковое представление встроено, и все, что вам нужно, это ToString()
.
Ответ 2
Вместо использования "ToDisplayString" просто переопределите ToString() вашего перечисления. Поэтому, если перечисление переопределяет это, оно примет это, в противном случае оно примет поведение ToString по умолчанию (в ToSelectList).
Ответ 3
Конечно, вы можете использовать DisplayAttribute для аннотирования ваших Enums.
enum OrderStatus
{
[Display(Description="Long Desc", Name="Awaiting Authorization", ShortName="Wait Auth")]
AwaitingAuthorization,
[Display(Description="...", Name="...", ShortName="...")]
InProduction,
[Display(Description="...", Name="...", ShortName="...")]
AwaitingDespatch
}
Я также создал метод расширения в моем перечислении, чтобы привести в порядок отображаемые значения в пользовательском интерфейсе.
Метод расширения:
public static class EnumExtensions
{
public static string ToName(this Enum enumValue)
{
var displayAttribute = enumValue.GetType()
.GetMember(enumValue.ToString())[0]
.GetCustomAttributes(false)
.Select(a => a as DisplayAttribute)
.FirstOrDefault();
return displayAttribute?.Name ?? enumValue.ToString();
}
}
С
public enum Test
{
[Display(Name="AAA")]
a,
b
}
Код:
Console.WriteLine(Test.a.ToName());
Console.WriteLine(Test.b.ToName());
Результаты
AAA
б
Я хочу связать свои перечисления с SelectList с помощью метода расширения:
Для безопасности типов я бы не использовал методы расширения, а вместо этого статический класс, который работает с типом Enum:
Версия до С# 7.3. Поскольку у нас нет Enum
до 7.3, это вызывает исключения во время выполнения, если мы пытаемся использовать значение, которое не является Enum
.
public static class Enums<TEnum>
where TEnum : struct, IComparable, IFormattable, IConvertible
{
static Enums()
{
if (!typeof(TEnum).IsEnum)
{
throw new InvalidOperationException();
}
}
}
Версия С# 7. 3+, с проверкой времени компиляции... ууу!
public static class Enums<TEnum>
where TEnum : Enum
{
}
Метод GetValues для класса:
public static IEnumerable<TEnum> GetValues(bool includeFirst)
{
var result = ((TEnum[])Enum.GetValues(typeof(TEnum)))
.ToList();
if (!includeZero)
{
result = result.Where(r => r != default).ToList();
}
return result;
}
Если вы следуете Рекомендациям по перечислению и включите значение по умолчанию (ноль), мы можем его игнорировать (иногда мы хотим отобразить значение, например "Нет выбранных", а иногда нет "Неправильный выбор").
Затем мы можем добавить еще один метод:
public static IEnumerable<string> GetNames(bool includeFirst)
{
var result = GetValue(includeFirst)
.Select(v => v.ToName())
.ToList();
return result;
}
Ответ 4
Вы можете сделать это:
public static string ToOrderStatusDisplayString(this Enum status)
{
switch ((OrderStatus)status)
{
...
}
}
Затем ограничьте TEnum Enum: where TEnum : System.Enum
Конечно, таким образом вы получаете кучу методов в самом Enum и теряете безопасность типов.