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));
        }
4

2 回答 2

13

不,基本上。非泛型类中的静态方法(例如 DropDownControl [no <>])是最好的方法,因为您应该能够在调用 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<,> 外观中?

于 2008-10-04T12:47:17.547 回答
0

如果T将始终KeyValuePair<TKey,TValue>存在,则根本不需要它是泛型类型参数。只需在您使用的任何地方使用实际类型T

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

for 的构造函数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引用设置为自身(基类包装)。然后应该通过的方法this应该通过Helper。这将确保当一个派生类实例纯粹是为了被包装而构建时,外部世界将永远不会收到对该派生实例的引用,而是会始终看到包装器。

于 2014-08-20T15:57:05.970 回答