How do I declare a type parameter for a generic type that is generic in itself?

Let's say that I want to implement an expiring cache and I want to make it shared in the underlying storage container. So I'll let the user tell the class which container type to use. But since I will be filling it with a private type, I need them to tell me the general type. So I am 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 must be a generic type, since I want to specialize it into a private type. I guess he used it like this:

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

      

This will cause the implementation to use a dictionary.

Is it possible? What is the syntax for this? If this is not possible, what is the best alternative for getting type safety on containers and this level of complexity?

+3


source to share


2 answers


No, C # generators don't work like that. It sounds like you want something like higher order lines and C # generators just don't work.

EDIT: Aargh, I just noticed what you are doing with type arguments.

No, in principle it won't work - you have to rethink your design. You could do it with reflection, but it would be better not to. It will be pretty frustrating.

EDIT: On reflection, you can 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 will look like this:

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 recreate the container, of course.)

Then your callers would have to implement IDictionaryFactory

- you could easily provide some simple implementations of course. This is what is usually simplified with delegates, but while the types of delegates can be generic, the method signature Invoke

inside the delegate cannot be - and that is what you want here.

+4


source


It won't work because the caller cannot create the dictionary <K, MyValue> - MyValue private

.

But it can be done if you do MyValue public

:

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

      

You want the Container to be IDictionary <K, MyValue <V -> and would like to be able to create new instances of the container, So you need the following constraint in the container type parameter:



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.

Usage example:

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

      

+5


source







All Articles