继承和共享静态字段

Rob*_*nik 8 c# generics inheritance static

假设我有一个Generic抽象类,它提供了一些默认的构造函数,所以你不必重复继承类中的代码:

public abstract class Base<TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TKey, TValue>> Values = new Dictionary<TKey, Base<TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是,当我写两个继承的类,如:

public sealed class One : Base<string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) {}
}

public sealed class Two : Base<string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) {}
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这两种实际上是类型安全的枚举.它们仅提供预定义值,并且不能为任何其他目的实例化它们.

题

问题是因为这两个继承的类都使用与基类相同的泛型类型Base<string, string>.泛型类和静态字段的工作方式是每个泛型类型(或多个组合时)创建一个新的静态字段.

在我的情况下,组合是string, string这就是为什么只有一个基类静态字段,它保存4个值而不是有两个静态字段,每个字段有2个值.

我如何克服这个问题,并将这些问题分开,同时仍然保持基类的这个功能,所以我不重复我的代码?

将字段从静态更改为实例将不起作用,因为它们最终会有4个实例,每个实例只保存一个值...

Aak*_*shM 9

这会奏效.我相信它被称为奇怪的重复模板模式,但不要引用我

public abstract class Base<TSelf, TKey, TValue>
{
    private static readonly IDictionary<TKey, Base<TSelf, TKey, TValue>> Values = 
        new Dictionary<TKey, Base<TSelf, TKey, TValue>>();

    public TKey Key { get; private set; }
    public TValue Value { get; private set; }

    protected Base(TKey key, TValue value)
    {
        this.Key = key;
        this.Value = value;

        Values.Add(key, this);
    }
}


public sealed class One : Base<One, string, string>
{
    public static readonly One Default = new One("DEF", "Default value");
    public static readonly One Invalid = new One("", "Invalid value");

    private One(string key, string value) : base(key, value) { }
}

public sealed class Two : Base<Two, string, string>
{
    public static readonly Two EU = new Two("EU", "European Union");
    public static readonly Two USA = new Two("", "United States of America");

    private Two(string key, string value) : base(key, value) { }
}
Run Code Online (Sandbox Code Playgroud)

'好奇'部分是定义One和Two被允许使用自己作为自己的类型参数!

  • 然而,它依赖于正确指定`TSelf`的人. (2认同)
  • 是的,这是一个黑客,但重要的是要注意它[不强制执行任何](http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx ). (2认同)

Ree*_*sey 6

当然,问题出现是因为您的两个类使用相同的泛型类型参数共享相同的基类.这意味着它们共享相同的静态成员.

我如何克服这个问题,并将这些问题分开,同时仍然保持基类的这个功能,所以我不重复我的代码?

一种方法是根据当前运行时类型在字典中构建Dictionary:

private static readonly IDictionary<Type, IDictionary<TKey, Base<TKey, TValue>>> Values = new Dictionary<Type, IDictionary<TKey, Base<TKey, TValue>>()>;


protected Base(TKey key, TValue value)
{
    this.Key = key;
    this.Value = value;

    IDictionary<TKey, Base<TKey, TValue>> dict;
    if (!Values.TryGetValue(this.GetType(), out dict)
    { 
        dict = new Dictionary<TKey, Base<TKey, TValue>>();
        Values[this.GetType()] = dict;
    }

    dict.Add(key, this);
}
Run Code Online (Sandbox Code Playgroud)

这确实导致所有查找都是2个阶段,因为您必须查找当前类型然后查找该字典中的值,但它根本不依赖于更改您的子类API.

  • 值得注意的是,这仍然是每个解析的泛型类型的一个静态字典,只是键入不同. (2认同)
  • @ReedCopsey是的我同意,我只是澄清了这个和其他答案之间的区别,这个答案实际上会产生两个静态变量,因为泛型类型的解析方式不同. (2认同)