Поведение для моделирования перечисления, реализующего интерфейс - PullRequest
13 голосов
/ 09 июня 2010

скажем, у меня есть перечисление что-то вроде:

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

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

У кого-нибудь есть идеи о том, как я мог бы реализовать что-то подобное?

Ответы [ 4 ]

27 голосов
/ 09 июня 2010

Есть ли веская причина использовать 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().

1 голос
/ 19 января 2019

Конечно, вы можете использовать DisplayAttribute для аннотирования ваших Enum s.

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

b

Я хочу связать свои перечисления с SelectList с помощью метода расширения:

Для безопасности типов я бы не использовал методы расширения, а вместо этого статический класс, который работает с типом Enum:

Версия до C # 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();
        }
    }
}

C # 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;
}

Если вы следуете Enum Guidelines и включаете значение по умолчанию (ноль), мы можем его игнорировать (иногда мы хотим отобразить значение, например «Нет выбранных», а иногда нет »Неверный выбор ").

Затем мы можем добавить другой метод:

public static IEnumerable<string> GetNames(bool includeFirst)
{
    var result = GetValue(includeFirst)
       .Select(v => v.ToName())
       .ToList();
    return result;
}
1 голос
/ 09 июня 2010

Вместо использования «ToDisplayString», просто переопределите ToString () вашего перечисления.Поэтому, если перечисление переопределяет его, оно примет его, в противном случае оно будет использовать поведение ToString по умолчанию (в ToSelectList).

0 голосов
/ 09 июня 2010

Вы можете сделать это:

public static string ToOrderStatusDisplayString(this Enum status)
{    
    switch ((OrderStatus)status)
    {
         ...
    }
}

Затем ограничить TEnum до Enum: where TEnum : System.Enum

Конечно, таким образом вы получите набор методов для самого Enum и потеряете типбезопасность.

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