带有“专用”构造函数的C#Generic Class

时间:2008-10-04 12:40:03

标签: c# generics

我有一个如下课程:

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 个答案:

答案 0 :(得分:12)

不,基本上。非泛型类中的静态方法(例如DropDownControl [no&lt;&gt;])是最好的方法,因为在调用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&lt;,&gt; - 我想知道你是否不能简化你的控制只是使用它,并强制非字典调用者将自己包装在IDictionary&lt;,&gt;中。门面?

答案 1 :(得分:0)

如果T始终为KeyValuePair<TKey,TValue>,则根本不需要它是通用类型参数。只需使用您使用的每个地方的实际类型T

否则,如果类型有时可能必须是其他类型,我建议你应该有一个基类型DropDownControl<TKey, TValue> : BaseControl,其类型的受保护字段Helper,以及虚拟实现几乎所有简单地在Helper上调用对应方的方法;在其中定义派生类HeldAs<TPair>,它使用“真实”实现覆盖所有方法。

DropDownControl<TKey,TValue>的构造函数将构造DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>>的新实例,并在Helper中存储对该实例的引用。然后,外部代码可以保存类型DropDownControl<TKey,TValue>的引用并使用它们而无需知道或关心密钥和值的保存方式。需要创建以不同方式存储事物并使用不同方法提取键和值的代码的代码可以调用DropDownControl<TKey,TValue>.HeldAs<actualStorageType>的构造函数,传递可以将actualStorageType转换为适当的键或值的函数。 / p>

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