2008-10-04 43 views
4

我有类似下面的类:C#泛型类“专业”构造

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

回答

12

不,基本上。在非泛型类的静态方法(如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)) { } 
} 

这增加了复杂性,并降低你的灵活性......我不会”做T这...

总体而言,这听起来像你想只IDictionary的<合作,> - 我不知道,如果你不能简化你的控制,只是利用这一点,并迫使非字典呼叫者包裹自己一个IDictionary <,>门面?

+0

那么我正在寻找一种方法来做类似于C++部分模板的特化。但似乎C#目前无法做到这一点(甚至没有技巧)。 – Fionn 2008-10-06 08:37:19

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密钥或值相应的功能代码。

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