C # Generic Class with custom constructor

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 convenient function for dictionaries (because they support all operations effectively on their own).

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

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

I am currently using the static Create function as a workaround, but I would like the direct constructor to be 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)); } 
+4
source share
2 answers

No, basically. A static method in a nonequivalent class (such as DropDownControl [no>]) is the best approach since you should be able to use type inference when calling Create () - i.e.

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

C # 3.0 helps here with both "var" (very welcome here) and with improved general type inference rules. In some (more general) cases, another similar option is the extension method, but the extension method for creating a very specific control from the dictionary is not very natural - I would use a method without extension.

Sort of:

 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 ...

 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 would not do this ...

In general, it looks like you only want to work with IDictionary <,> - I wonder if you can simplify your control to just use this and force non-dictionary callers to wrap themselves in an IDictionary <,> facade?

+12
source

If T will always be KeyValuePair<TKey,TValue> , there is no need for it to be a typical type parameter in general. Just use the actual type where you use T

Otherwise, if the type can sometimes be something else, I would suggest that you should have the base type DropDownControl<TKey, TValue> : BaseControl with a Helper protected field of the same type and virtual implementations of almost all the methods that just call their copies on Helper ; inside which the derived class HeldAs<TPair> , which overrides all methods with "real" implementations.

The constructor for DropDownControl<TKey,TValue> will create a new instance of DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>> and save the link to it in Helper . External code may contain links of the type DropDownControl<TKey,TValue> and use them without knowing or caring about how keys and values ​​were saved. Code that needs to create something that stores things differently and uses different methods to extract keys and values ​​can call the DropDownControl<TKey,TValue>.HeldAs<actualStorageType> , passing functions that can convert actualStorageType to the appropriate keys or values.

If any of the DropDownControl<TKey,TValue> had to pass this , then the DropDownControl<TKey,TValue>.HeldAs<TStorage> should install Helper for itself, but the base type constructor should build a reference to the instance type and set a reference to Helper instance on itself (shell of the base class). Then the methods that pass this will pass Helper . This ensures that when an instance of a derived class is created solely to be wrapped, the outside world will never receive a reference to that derived instance, but will see the shell sequentially.

0
source

Source: https://habr.com/ru/post/1277469/


All Articles