Какой правильный способ получения моих пользовательских классов перечисления по их значению?

2

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

public abstract class Enumeration<X, Y> : IComparable where X : IComparable
{

    public Enumeration(X value, Y displayName) { }

    public Y DisplayName { get { return _displayName; } }
    public X Value { get { return _value; } }

}

И класс, который наследует его, будет:

public class JobType : Enumeration<string, string>
{
    public static JobType ChangeOver = new JobType("XY01", "Changeover");
    public static JobType Withdrawal = new JobType("XY02", "Withdrawal");
    public static JobType Installation = new JobType("XY03", "Installation");

    private JobType(string jobTypeId, string description)
        : base(jobTypeId, description) { }
}

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

public static JobType Resolve(string jobTypeId) { return matchingJobType; }

Я начал писать метод разрешения для каждого класса перечисления, но должен быть лучший способ, чем дублирование того же метода с оператором switch в каждом?

Я думал о добавлении свойства Dictionary<X, Enumeration<X, Y>> Cache; в базовый класс и добавлении в него класса из конструктора базового класса. Это также обеспечило бы уникальные ценности. Проблема с этим заключается в том, что когда я получаю перечисление из Словаря, это тип Enumeration<X, Y>, и я хочу его как JobType.

Таким образом, это означает, что нужно либо добавить третий общий тип в класс Enumeration, либо иметь:

public static T Resolve(X value); // With the additional type
public static T Resolve<T>(X value); // Or without an additional type

Мне явно не нравится идея писать JobType.Resolve<JobType>(foo);, и я хочу просто JobType.Resolve(foo);, но это должно быть сделано с минимальным количеством кода. То есть может ли это просто быть обработано из базового класса без необходимости включать дополнительный общий тип?

  • 0
    Есть ли когда-нибудь ситуация, когда Y - это что-то еще, кроме строки?
  • 0
    @Henk - Да, но, вероятно, это никогда не будет более сложным, чем int.
Теги:
generics
c#-4.0
static-methods

2 ответа

3
Лучший ответ

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

public abstract class Enumeration<TEnum, X, Y> : IComparable 
    where TEnum : Enumeration<TEnum, X, Y>
    where X : IComparable
{
    public static TEnum Resolve(X value) { /* your lookup here */ }

    // other members same as before; elided for clarity
}

Затем вы определяете свои конкретные классы следующим образом.

public class JobType : Enumeration<JobType, string, string>
{
    // other members same as before; elided for clarity
}

Теперь ваши типы будут совпадать и не будут выполняться кастинг для метода базового класса Resolve.

JobType type = JobType.Resolve("XY01");

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

  • 0
    Есть ли способ добиться того же, но без дополнительного универсального типа?
  • 0
    @ GenericTypeTea - Не то, чтобы я мог думать, нет.
Показать ещё 1 комментарий
0

Ваш реальный класс перечисления может быть более сложным, чем это, но ваша текущая реализация выглядит так, что ее можно было бы определить гораздо проще, как стандартное перечисление в сочетании с шаблоном DAL или просто словарь:

public enum JobType
{
    ChangeOver,
    Withdrawal,
    Installation,
}

// Maybe inside some DAL-pattern/database parsing class:
var databaseToJobTypeMap = new Dictionary<string, JobType>()
{
    { "XY01", JobType.ChangeOver },
    // ...
};

Сохранение кода анализа вместе (возможно, с абстракцией интерфейса) дает вам возможность переключать парсеры при изменении/изменении формата хранения данных или при наличии нескольких источников данных.

Если вам нужно проанализировать фактические значения JobType (например, строку "ChangeOver" или целочисленное представление), вы можете использовать Enum.Parse/Enum.TryParse или casting.

Реализация, которая у вас есть, выглядит более негибкой, поскольку она блокирует тип перечисления одним стилем/представлением отображения словаря.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню