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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/170272/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 16:32:36  来源:igfitidea点击:

C# Generic Class with "specialized" constructor

c#generics

提问by Fionn

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:

但问题是这样的构造函数只会指定 Key 和 Value 而不会直接指定 T,而 T 只是 KeyValuePair。有没有办法告诉编译器这个构造函数 T 是 KeyValuePair,比如:

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.

目前我使用静态 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));
        }

采纳答案by Marc Gravell

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.

不,基本上。非泛型类中的静态方法(例如 DropDownControl [no <>])是最好的方法,因为您应该能够在调用 Create() 时使用类型推断 - 即

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.

C# 3.0 在这里通过“var”(这里非常受欢迎)和大大改进的泛型类型推断规则提供帮助。在某些(更一般的)情况下,另一个类似的选项是扩展方法,但是从字典创建非常具体的控件的扩展方法感觉不太自然 - 我会使用非扩展方法。

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 wantto 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?

总的来说,听起来您只想使用 IDictionary<,> - 我想知道您是否不能简化您的控制以仅使用它,并强制非字典调用者将自己包装在 IDictionary<,> 门面中?

回答by supercat

If Twill always be KeyValuePair<TKey,TValue>there's no need for it to be a generic type parameter at all. Just use the actual type everyplace you use T.

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

Otherwise, if the type may sometimes have to be something else, I would suggest that you should perhaps have a base type DropDownControl<TKey, TValue> : BaseControlwith a protected field Helperof the same type, and virtual implementations of nearly all methods which simply invoke their counterparts on Helper; within that define a derived class HeldAs<TPair>which overrides all the methods with "real" implementations.

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

The constructor for DropDownControl<TKey,TValue>would construct a new instance of DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>>and store a reference to that in Helper. Outside code could then hold references of type DropDownControl<TKey,TValue>and use them without having to know or care how keys and values were held. Code which needs to create something that stores things a different way and uses different methods to extract keys and values could call the constructor of DropDownControl<TKey,TValue>.HeldAs<actualStorageType>, passing functions which can convert actualStorageTypeto keys or values as appropriate.

for 的构造函数DropDownControl<TKey,TValue>将构造一个 的新实例DropDownControl<TKey,TValue>.HeldAs<KeyValuePair<TKey,TValue>>并将对该实例的引用存储在 中Helper。然后,外部代码可以保存类型引用DropDownControl<TKey,TValue>并使用它们,而不必知道或关心键和值是如何保存的。需要创建以不同方式存储事物并使用不同方法提取键和值的代码可以调用 的构造DropDownControl<TKey,TValue>.HeldAs<actualStorageType>函数,传递可以actualStorageType适当转换为键或值的函数。

If any of the methods of DropDownControl<TKey,TValue>would be expected to pass this, then the constructor of DropDownControl<TKey,TValue>.HeldAs<TStorage>should set Helperto itself, but the constructor of the base type, after constructing the derived-type instance, should set the derived instance's Helperreference to itself (the base-class wrapper). The methods which would pass thisshould then pass Helper. That will ensure that when a derived-class instance is constructed purely for the purpose of being wrapped, the outside world will never receive a reference to that derived instance, but will instead consistently see the wrapper.

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