简体   繁体   English

如何声明泛型类型的类型参数本身是通用的?

[英]How to declare type parameter for a generic type which is itself generic?

Let's say that I want to implemented an expiring cache, and I want to make it generic on the underlying storage container. 假设我想实现一个过期的缓存,我想在底层存储容器上使它成为通用的。 So I'll allow the user to tell my class which container type to use. 所以我将允许用户告诉我的班级要使用哪种容器类型。 But since I'll be filling it with a private type, I need them to tell me a generic type. 但由于我将填充私有类型,我需要它们告诉我一个通用类型。 So I'm trying to do something like this: 所以我想尝试做这样的事情:

class MyCache<K, V, Container> : IDictionary<K, V>
    where Container : // what goes here?
{
    private class MyValue
    {
        public readonly V Value;
        public readonly DateTime InsertionTime;
    }

    private IDictionary<K, MyValue> m_dict = new Container<K, MyValue>(); // a specialization of the generic container

    // implement IDictionary<K, V>, checking for value expiration
    public bool TryGetValue(K key, out V val)
    {
        MyValue myval;
        if (m_dict.TryGetValue(key, out myval))
        {
            if (Expired(myval.InsertionTime))
            {
                m_dict.Remove(key);
            }
            else
            {
                val = myval.Value;
                return true;
            }
        }

        // not there or expired
        val = default(V);
        return false;
    }
}

So Container needs to be a generic type since I want to specialize it on a private type. 所以Container需要是泛型类型,因为我想将它专门化为私有类型。 I imagine using it like this: 我想像这样使用它:

var cache = new MyCache<String, String, Dictionary>();

Which would cause the implementation to use Dictionary. 哪会导致实现使用Dictionary。

Is this possible? 这可能吗? What is the syntax for it? 它的语法是什么? If it's not possible, what is the best alternative to get type safety on the containers and this level of composability? 如果不可能,那么在容器上获得类型安全性和这种可组合性的最佳选择是什么?

This won't work, because the caller cannot create a Dictionary<K, MyValue> — MyValue is private . 这不起作用,因为调用者无法创建Dictionary <K,MyValue> - MyValue是private

But it can be done if you make MyValue public : 但是如果你public MyValue就可以做到:

public class MyValue<V>
{
    public readonly V Value;
    public readonly DateTime InsertionTime;
}

You want the Container to be a IDictionary<K, MyValue<V>> and want to be able to create new Container instances. 您希望Container为IDictionary <K,MyValue <V >>并希望能够创建新的Container实例。 So you need the following constraints in the Container type parameter: 因此,您需要在Container类型参数中使用以下约束

class MyCache<K, V, Container> : IDictionary<K, V>
    where Container : IDictionary<K, MyValue<V>>, new()
{
    private IDictionary<K, MyValue<V>> m_dict = new Container();
}

Note that the IDictionary<K, V> interface does not provide a TryGetValue Method. 请注意, IDictionary <K,V>接口不提供TryGetValue方法。

Example usage: 用法示例:

var cache = new MyCache<string, int, Dictionary<string, MyValue<int>>>();

No, C# generics don't work like that. 不,C#泛型不能那样工作。 It sounds like you want something along the lines of higher order types, and C# generics just don't work that way. 听起来你想要更高阶类型的东西,而C#泛型就不会那样工作。

EDIT: Aargh, I've just noticed what you're doing with the type arguments. 编辑:Aargh,我刚刚注意到你正在使用类型参数做什么。

No, basically that won't work at all - you should rethink your design. 不,基本上根本不起作用 - 你应该重新考虑你的设计。 You could do it with reflection, but it would be better not to. 可以用反射来做,但最好不要。 It's going to get pretty nasty. 它会变得非常讨厌。

EDIT: On reflection, you could do this by passing in a factory which then has a generic method: 编辑:反思,你可以通过传入一个工厂,然后有一个通用的方法做到这一点:

public interface IDictionaryFactory
{
    IDictionary<TKey, TValue> CreateDictionary<TKey, TValue>();
}

Then your code would be: 然后你的代码将是:

class MyCache<K, V> : IDictionary<K, V>
{
    private class MyValue
    {
        public readonly V Value;
        public readonly DateTime InsertionTime;
    }

    private IDictionary<K, MyValue> m_dict;

    public MyCache(IDictionaryFactory dictionaryFactory)
    {
        m_dict = dictionaryFactory.CreateDictionary<K, MyValue>();
    }

    ...
}

(You could remember the factory if you needed to be able to recreate the container, of course.) (当然,如果你需要能够重新创建容器,你可以记住工厂。)

Your callers would then have to implement IDictionaryFactory - you could easily provide some simple implementations of course. 然后您的调用者必须实现IDictionaryFactory - 您当然可以轻松地提供一些简单的实现。 This is the sort of thing which is normally made simpler with delegates, but while delegate types can be generic, the signature of the Invoke method within a delegate can't be - and that's what you want here. 这是通常使用委托更简单的事情,但是委托类型可以是通用的,委托中的Invoke方法的签名不能 - 这就是你想要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM