假设我想实现一个过期的缓存,我想在底层存储容器上使它成为通用的。所以我将允许用户告诉我的班级要使用哪种容器类型。但由于我将填充私有类型,我需要它们告诉我一个通用类型。所以我试着做这样的事情:
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;
}
}
所以Container需要是泛型类型,因为我想在私有类型上专门化它。我想像这样使用它:
var cache = new MyCache<String, String, Dictionary>();
这会导致实现使用Dictionary。
这可能吗?它的语法是什么?如果不可能,那么在容器上获得类型安全性和这种可组合性的最佳选择是什么?
答案 0 :(得分:5)
这不起作用,因为调用者无法创建字典&lt; K,MyValue&gt; - MyValue为private
。
但如果你制作MyValue public
:
public class MyValue<V>
{
public readonly V Value;
public readonly DateTime InsertionTime;
}
您希望Container为IDictionary<K, MyValue<V>>并希望能够创建新的Container实例。因此,您需要在Container类型参数中使用以下constraints:
class MyCache<K, V, Container> : IDictionary<K, V>
where Container : IDictionary<K, MyValue<V>>, new()
{
private IDictionary<K, MyValue<V>> m_dict = new Container();
}
请注意,IDictionary<K, V>接口不提供TryGetValue方法。
示例用法:
var cache = new MyCache<string, int, Dictionary<string, MyValue<int>>>();
答案 1 :(得分:4)
不,C#泛型不能那样工作。听起来你想要更高阶类型的东西,而C#泛型就不会那样工作。
编辑:Aargh,我刚刚注意到你在使用类型参数做了什么。不,基本上根本不起作用 - 你应该重新考虑你的设计。你可以用反射来做,但最好不要这样做。它会变得非常讨厌。
编辑:经过反思,你可以通过传入一个具有通用方法的工厂来做到这一点:
public interface IDictionaryFactory
{
IDictionary<TKey, TValue> CreateDictionary<TKey, TValue>();
}
然后你的代码将是:
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>();
}
...
}
(当然,如果你需要能够重新创建容器,你可以记住工厂。)
然后您的呼叫者必须实施IDictionaryFactory
- 您当然可以轻松提供一些简单的实施方案。这种情况通常使委托更简单,但委托类型可以是通用的,委托中Invoke
方法的签名可以不是 - 这就是你想要的。