C# 泛型类型约束取决于使用的构造函数

C# generic type constraint depending on used constructor

我想在 C# 中模仿 Python collections.defaultdict。只要值类型具有无参数构造函数,以下内容就可以正常工作:

public class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : new()
{
    public new TValue this[TKey key]
    {
        get
        {
            TValue val;
            if (!TryGetValue(key, out val)) {
                val = new TValue();
                Add(key, val);
            }
            return val;
        }
        set { base[key] = value; }
    }
}

但是如果我想使用一个以键为参数的构造函数呢?或者一般来说,一个工厂函数,给定键,returns 值类型的实例?

public class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : new()
{
    private readonly Func<TKey, TValue> factory;

    public DefaultDictionary() : this(key => new TValue())
    {}

    public DefaultDictionary(Func<TKey, TValue> factory)
    {
        this.factory = factory;
    }

    public new TValue this[TKey key]
    {
        get
        {
            TValue val;
            if (!TryGetValue(key, out val)) {
                val = factory(key);
                Add(key, val);
            }
            return val;
        }
        set { base[key] = value; }
    }
}

现在的问题是 TValue 仍然需要有一个无参数的构造函数,即使使用工厂函数也是如此。如果 Thingy 没有无参数构造函数,以下将无法编译:

new DefaultDictionary<int, Thingy>(key => new Thingy(key, otherStuff, moreStuff));

但是,删除约束会导致 new TValue() 语句中出现错误 CS0304。

直觉上我想将类型约束放在 DefaultDictionary() 构造函数上,但我怀疑这是可能的。在 C# 中有解决这个问题的正确方法吗?

一种方法是只保留 DefaultDictionary 中采用值工厂的构造函数,并创建一个具有 : new() 约束的子类,并将无参数构造函数移到那里:

public class DefaultDictionaryParameterless<TKey, TValue> :
    DefaultDictionary<TKey, TValue> where TValue : new()
{
    public DefaultDictionaryParameterless()
        : base(x => new TValue())
    {
        
    }
}

public class DefaultDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    private readonly Func<TKey, TValue> factory;

    public DefaultDictionary(Func<TKey, TValue> factory)
    {
        this.factory = factory;
    }
    ...
}