C#带有“专门化”构造函数的泛型类

4

我有一个如下所示的类:

public class DropDownControl<T, Key, Value> : BaseControl
    where Key: IComparable
{
    private IEnumerable<T> mEnumerator;
    private Func<T, Key> mGetKey;
    private Func<T, Value> mGetValue;
    private Func<Key, bool> mIsKeyInCollection;

    public DropDownControl(string name, IEnumerable<T> enumerator, Func<T, Key> getKey, Func<T, Value> getValue, Func<Key, bool> isKeyInCollection)
        : base(name)
    {
        mEnumerator = enumerator;
        mGetKey = getKey;
        mGetValue = getValue;

        mIsKeyInCollection = isKeyInCollection;
    }

我想为字典添加一个方便的函数(因为字典自身支持所有操作的高效性)。

但问题在于这样的构造函数只会指定Key和Value,而不是直接指定T,而T只是KeyValuePair。是否有一种方法告诉编译器这个构造函数的T是KeyValuePair,例如:

public DropDownControl<KeyValuePair<Key, Value>>(string name, IDictionary<Key, Value> dict) { ... }

目前我使用静态Create函数作为解决方法,但我更喜欢直接使用构造函数。

public static DropDownControl<KeyValuePair<DKey, DValue>, DKey, DValue> Create<DKey, DValue>(string name, IDictionary<DKey, DValue> dictionary)
            where DKey: IComparable
        {
            return new DropDownControl<KeyValuePair<DKey, DValue>, DKey, DValue>(name, dictionary, kvp => kvp.Key, kvp => kvp.Value, key => dictionary.ContainsKey(key));
        }
2个回答

13

不是的,基本上不需要。在非泛型类(比如DropDownControl [没有<>])中使用静态方法是最好的方法,因为您应该能够在调用Create()时使用类型推断 - 即:

var control = DropDownControl.Create(name, dictionary);

C# 3.0通过“var”(非常受欢迎)和改进的泛型类型推断规则在这方面提供了帮助。在某些(更一般的)情况下,另一个类似的选项是扩展方法,但是从字典中创建一个非常特定的控件的扩展方法并不感觉很自然 - 我会使用非扩展方法。

例如:

public static class DropDownControl
{
    public static DropDownControl<KeyValuePair<TKey,TValue>, TKey, TValue>
            Create<TKey,TValue>(IDictionary<TKey, TValue> value, string name)
    where TKey : IComparable
    {
        return new DropDownControl<KeyValuePair<TKey, TValue>, TKey, TValue>
            (name, value, pair => pair.Key, pair => pair.Value,
            key => value.ContainsKey(key)
        );
    }
}

另一个选择是继承,但我不太喜欢它...

public class DropDownControl<TKey, TValue> :
    DropDownControl<KeyValuePair<TKey, TValue>, TKey, TValue>
    where TKey : IComparable
{
    public DropDownControl(IDictionary<TKey, TValue> lookup, string name)
        : base(name, lookup, pair => pair.Key, pair => pair.Value,
            key => lookup.ContainsKey(key)) { }
}

这样做会增加复杂性,降低您的灵活性... 我不会这样做...

总体而言,听起来您只使用IDictionary<,> - 我想知道是否可以简化您的控制,只使用此功能,并强制非字典调用者将自己包装在一个IDictionary<,>外观中?


我一直在寻找一种类似于C++的部分模板特化的方法。但是看起来C#目前无法做到这一点(即使使用技巧也不行)。 - Fionn
事实上,泛型不适合模板特化。 - Marc Gravell

0
如果 T 总是 KeyValuePair<TKey,TValue>,那么根本不需要将其作为泛型类型参数。只需在使用 T 的每个地方使用实际类型即可。
否则,如果该类型有时可能需要是其他类型,则建议您应该有一个基类型 DropDownControl<TKey, TValue> : BaseControl,其中包含一个受保护的字段 Helper,类型与之相同,并且几乎所有方法都有虚拟实现,仅简单地调用其在 Helper 上的对应方法;在其中定义一个派生类 HeldAs<TPair>,它使用“真实”的实现覆盖了所有方法。 < p > DropDownControl<TKey,TValue> 的构造函数将构造一个新的实例 DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>> 并将其引用存储在 Helper 中。外部代码可以持有类型为 DropDownControl<TKey,TValue> 的引用,并在不知道或不关心键和值如何保存的情况下使用它们。需要创建以不同方式存储东西并使用不同方法提取键和值的代码可以调用 DropDownControl<TKey,TValue>.HeldAs<actualStorageType> 的构造函数,传递可以将 actualStorageType 转换为相应键或值的函数。

如果 DropDownControl<TKey,TValue> 的任何方法都希望传递 this,那么 DropDownControl<TKey,TValue>.HeldAs<TStorage> 的构造函数应该将 Helper 设置为自身,但是基类型的构造函数在构造派生类型实例后,应该将派生实例的 Helper 引用设置为自身(即基类包装器)。然后会传递 Helper 的方法应该传递 this。这将确保当纯粹为包装目的而构造派生类实例时,外部世界永远不会接收到对该派生实例的引用,而始终会看到包装器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接