关于c#:有一个奇怪的重复模板模式的替代品吗?

Is there an alternative to the Curiously Recurring Template Pattern?

在过去的几周里,我用奇怪的重复模板模式给自己带来了一些头痛。

以下是我的两个问题:

  • 正确的检索方法是什么我的自定义枚举类它们的价值?
  • 为什么我的静态物体不是当首次访问静态类是上的静态方法基类?

如何改进以下示例:

1
2
3
4
5
6
7
8
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类中重复:

1
2
3
4
5
6
7
8
9
10
11
12
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.Resolve(X value);的调用不会导致DocketType静态对象被实例化。

我并不反对从头重写这个。我知道这是一种大代码味道。目前,为了实现这一点,我的基类使用了受保护的静态方法ChildResolve,并且我将Resolve添加到了我的每个枚举类中。讨厌的东西!

答:

似乎没有什么好的模式可供选择,所以我坚持这个模式,从公认的答案中获得灵感,并提出了这个问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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示例项目中使用的相同代码,因此使用它时我感觉不太脏!


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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();
    }
}


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

一个快速组合的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 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());
    }
}


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

1
2
3
4
static Enumeration()
{
    RuntimeHelpers.RunClassConstructor(typeof(TEnum).TypeHandle);
}

您需要对"给定类型的所有子类"执行一些操作。如果不使用appdomain.current.getassembles()并对其进行迭代,任何此类性质的操作都是不可能的。如果采用这种方法,则可以通过创建仅应用于程序集(以及应包含在子类搜索中的其他属性)的程序集级属性来优化性能,并在准备对每个程序集调用.gettypes()时使用该属性。

很明显,下面是一个获得所有这些子类的示例:

1
2
3
4
5
6
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<,,>));

从这里开始,在每个系统上使用反射应该是一个简单的问题,输入静态字段并做您想做的事情。