代码之家  ›  专栏  ›  技术社区  ›  Fionn

C带有“专用”构造函数的泛型类

  •  4
  • Fionn  · 技术社区  · 16 年前

    我有一个如下的班级:

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

    我想为字典添加一个方便的函数(因为它们自己有效地支持所有操作)。

    但问题是这样的构造函数只指定键和值,而不直接指定t,但t只是keyValuePair。有没有一种方法可以告诉编译器这个构造函数t是keyValuePair,比如:

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

    目前,我使用静态创建函数作为解决方法,但我更希望直接构造函数。

    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 回复  |  直到 10 年前
        1
  •  12
  •   Marc Gravell    16 年前

    不,基本上。非泛型类(如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外观中?

        2
  •  0
  •   supercat    10 年前

    如果 T 将永远 KeyValuePair<TKey,TValue> 它根本不需要是泛型类型参数。只要使用你使用的每一个地方的实际类型 T .

    否则,如果类型有时必须是其他类型,我建议您可能应该有一个基类型 DropDownControl<TKey, TValue> : BaseControl 带保护字段 Helper 同一类型的,几乎所有方法的虚拟实现,这些方法只是在 帮手 ;定义派生类 HeldAs<TPair> 它用“实际”实现覆盖所有方法。

    的构造函数 DropDownControl<TKey,TValue> 将构造的新实例 DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>> 并将其存储在 帮手 . 然后外部代码可以保存类型的引用 下拉控件<tkey,tvalue> 使用它们时,不必知道或关心如何持有密钥和值。需要创建以不同方式存储内容并使用不同方法提取键和值的代码可以调用 DropDownControl<TKey,TValue>.HeldAs<actualStorageType> ,传递可转换的函数 actualStorageType 根据需要设置键或值。

    如果有任何方法 下拉控件<tkey,tvalue> 希望通过 this ,然后是的构造函数 DropDownControl<TKey,TValue>.HeldAs<TStorage> 应该设置 帮手 但在构造派生类型实例之后,基类型的构造函数应设置派生实例的 帮手 对自身的引用(基类包装器)。通过的方法 然后应该通过 帮手 . 这将确保当一个派生类实例纯粹是为了被包装而构造时,外部世界将永远不会收到对该派生实例的引用,而是一致地看到包装器。