Есть ли альтернатива шаблону любопытно повторяющегося шаблона? - PullRequest
3 голосов
/ 11 августа 2010

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

Исходя из этих двух моих вопросов:

Как улучшить следующий пример:

public class DocketType : Enumeration<DocketType, int, string>
{
    public static DocketType Withdrawal = new DocketType(2, "Withdrawal");
    public static DocketType Installation = new DocketType(3, "Installation");

    private DocketType(int docketTypeId, string description) 
        : base(docketTypeId, description) { }
}

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

public abstract class Enumeration<TEnum, X, Y> : IComparable 
    where TEnum : Enumeration<TEnum, X, Y> 
{        
    protected Enumeration(X value, Y displayName)
    {
        AddToStaticCache(this);
    }
    public static TEnum Resolve(X value)
    {
        return Cache[value] as TEnum;
    }
}

Проблема с этим, как вы увидите из моего второго связанного вопроса, состоит в том, что вызов Enumeration<DocketType, int, string>.Resolve(X value); не вызывает создания экземпляров DocketType статических объектов.

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

ОТВЕТ:

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

static Enumeration()
{
    GetAll();
}

public static void GetAll()
{
    var type = typeof(TEnum);
    var fields = type.GetFields(BindingFlags.Public | 
        BindingFlags.Static | BindingFlags.DeclaredOnly);

    foreach (var info in fields)
    {
        var locatedValue = info.GetValue(null) as Enumeration<TEnum, X, Y>;
        Cache.Add(locatedValue.Value, locatedValue);
    }
}

Это тот же код, который используется в примере проекта CodeCampServer MVC, поэтому я чувствую себя менее грязным при его использовании!

Ответы [ 4 ]

3 голосов
/ 11 августа 2010

Это не очень элегантно, но что-то вроде этого может помочь:

public class DocketType : Enumeration<DocketType, int, string>
{
    public static readonly DocketType Withdrawal =
        new DocketType(2, "Withdrawal");

    public static readonly DocketType Installation =
        new DocketType(3, "Installation");

    private DocketType(int docketTypeId, string description)
        : base(docketTypeId, description) { }
}

public abstract class Enumeration<TEnum, TId, TDescription> : IComparable
    where TEnum : Enumeration<TEnum, TId, TDescription>
{
    private static readonly Dictionary<TId, TEnum> _cache;

    static Enumeration()
    {
        Type t = typeof(TEnum);
        _cache = t.GetFields(BindingFlags.Public | BindingFlags.Static)
                  .Where(f => f.FieldType == t)
                  .Select(f => (TEnum)f.GetValue(null))
                  .ToDictionary(e => e.Id, e => e);
    }

    public static TEnum Resolve(TId id)
    {
        return _cache[id];
    }

    public TId Id { get; private set; }
    public TDescription Description { get; private set; }

    protected Enumeration(TId id, TDescription description)
    {
        Id = id;
        Description = description;
    }

    // IComparable
    public int CompareTo(object obj)
    {
        // TODO
        throw new NotImplementedException();
    }
}
2 голосов
/ 11 августа 2010

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

Пример быстрого соединения:

// The Collection of values to be enumerated
public class DocketEnum : EnumarationCollection<DocketType, int, string>
{
        // Values are fields on a statically instanced version of this class
    public DocketType Withdrawal = new DocketType(2, "Withdrawal");
    public DocketType Installation = new DocketType(3, "Installation");

    // The publicly accessible static enumeration 
    public static DocketEnum Values = new DocketEnum();
}

// The actual value class
public class DocketType : EnumerationValue<DocketType, int, string>
{
        // Call through to the helper base constructor
    public DocketType(int docketTypeId, string description) 
        : base(docketTypeId, description) { }
}

// Base class for the enumeration
public abstract class EnumarationCollection<TType, X, Y>
    where TType : EnumerationValue<TType, X, Y> 
{
            // Resolve looks at the static Dictionary in the base helpers class
    public TType Resolve(X value)
    {
        return Cache[value] as TType;
    }

    public static Dictionary<X, EnumerationValue<TType, X, Y> > Cache = new Dictionary<X, EnumerationValue<TType, X, Y>>();
}

// Base class for the value
public abstract class EnumerationValue<TType, X, Y> 
    where TType : EnumerationValue<TType, X, Y> 
{        
        // helper constructer talks directly the the base helper class for the Enumeration
    protected EnumerationValue(X value, Y displayName)
    {
        EnumarationCollection<TType, X,Y >.Cache.Add(value, this as TType);
    }
}



class MainClass
{
    public static void Main (string[] args)
    {
                    // You can immediately resolve to the enumeration
        Console.WriteLine(DocketEnum.Values.Resolve(2).ToString());
    }
}
0 голосов
/ 12 августа 2010

Если вы хотите принудительно запустить статический конструктор другого класса, вы можете использовать RuntimeHelpers.RunClassConstructor .Вы можете вызвать его из статического конструктора Enumeration<TEnum, X, Y>, чтобы он запускался при первом использовании статического метода при любом создании универсального типа:

static Enumeration()
{
    RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle);
}
0 голосов
/ 11 августа 2010

Вы хотите сделать что-то "для всех подклассов данного типа".Ничего подобного не возможно без использования AppDomain.Current.GetAssemblies () и итерации по ним.Если вы воспользуетесь этим подходом, вы сможете оптимизировать производительность, создав атрибут уровня сборки, который будет применяться только к вашим сборкам (и другим, которые должны быть включены в поиск по подклассам.) И использовать его при подготовке к вызову .GetTypes ()на каждой сборке.

Для ясности, вот пример того, как могут выглядеть все эти подклассы:

Type[] subclasses = AppDomain.CurrentDomain.GetAssemblies()
    .Where(x => Attribute.IsDefined(typeof(MyEnumeartionAssemblyAttribute)))
    .SelectMany(x => x.GetTypes())
    .Where(x => x.BaseType != null && 
           x.BaseType.IsGenericType && 
           x.BaseType.GetGenericTypeDefinition() == typeof(Enumeration<,,>));

Оттуда должно быть просто вопрос использования отражения в каждом System.Type и выполнениячто вы будете со статическими полями.

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