tags:

views:

4965

answers:

1

I have a class like the following:

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

And I want to add a convenience function for Dictionaries (because they support all operations efficiently on their own).

But the problem is that such a constructor would only specify Key and Value but not T directly, but T is just KeyValuePair. Is there a way to tell the compiler for this constructor T is KeyValuePair, like:

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

Currently I use a static Create function as workaround, but I would like a direct constructor better.

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));
        }
+8  A: 

No, basically. The static method in a non-generic class (such as DropDownControl [no <>]) is the best approach, as you should be able to use type-inference when you call Create() - i.e.

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

C# 3.0 helps here both via "var" (very welcome here) and by the much-improved generic type inference rules. In some (more general) case, another similar option is an extension method, but an extension method to create a very specific control from a dictionary doesn't feel very natural - I'd use a non-extension method.

Something like:

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

Another option is inheritance, but I don't like it much...

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

This adds complexity and reduces your flexibility... I wouldn't do this...

Overall, it sounds like you want to be working with just IDictionary<,> - I wonder if you can't simplify your control to just use this, and force non-dictionary callers to wrap themselves in an IDictionary<,> facade?

Marc Gravell
Well i was searching for a way to do something like C++ partial template specialization. But it seems C# currently can't do that (not even with tricks).
Fionn
But the IDictionary facade is a an good idea anyway.
Fionn
Indeed; generics do not lend themselves to template specialization
Marc Gravell