代码之家  ›  专栏  ›  技术社区  ›  djdd87

有没有一种替代这种奇怪的循环模板模式的方法?

  •  3
  • djdd87  · 技术社区  · 14 年前

    curiously recurring template 图案。

    如何改进以下示例:

    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);
        }
    }
    

    这也是codecampservermvc示例项目中使用的相同代码,因此我觉得使用它没有那么麻烦!

    4 回复  |  直到 7 年前
        1
  •  3
  •   LukeH    14 年前

    这不是很优雅,但像这样的东西可能会起作用:

    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
  •  2
  •   Nicholas M T Elliott    14 年前

    您需要将静态字段推入一个类中,该类将静态实例作为实例字段。这样,就可以通过一个静态成员访问枚举,该静态成员会立即引用所有枚举成员。

    // 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());
        }
    }
    
        3
  •  0
  •   Kirk Woll    14 年前

    你想“对给定类型的所有子类”做点什么。如果不使用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<,,>));
    

    从这里开始,在每个系统上使用反射应该是一个简单的问题。

        4
  •  0
  •   Quartermeister    14 年前

    如果要强制运行另一个类的静态构造函数,可以使用 RuntimeHelpers.RunClassConstructor Enumeration<TEnum, X, Y> 以便在您第一次对泛型类型的任何实例化使用静态方法时运行它:

    static Enumeration()
    {
        RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle);
    }