我想知道是否有办法存储在泛型类型的Dictionary/List/...
中。
让我们想象一下这个类:
public class Registry{
private Dictionary<String, MyGenericType<IContainableObject>> m_elementDictionary = new Dictionary<String, MyGenericType<IContainableObject>>();
public void Register<T>(MyGenericType<T> objectToRegister)
where T: IContainableObject
{
m_elementDictionary[objectToRegister.key] = objectToRegister; //This doesn't work
}
}
我不明白为什么我们不能将此元素添加到Dictionary
中,因为我们知道我们使用泛型类型收到的参数实际上是由于 where 条件而MyGenericType<IContainableObject>
。
请注意:
- 我知道我可以在商店
MyGenericType<IContainableObject>
放置一个接口,这是一本字典。这是主题。 - 我知道我可以有一个
MyGenericType<IContainableObject>
论点,这也是重点。
我更想知道协方差/逆变是否可以在这里提供帮助?
你应该像这样表达 where 条件:
public void Register<T>(T objectToRegister)
where T : MyGenericType<IContainableObject> {
m_elementDictionary[objectToRegister.key] = objectToRegister;
}
此外,您应该将MyGenericType
定义为协变,如以下示例所示:
interface IContainableObject {
}
public interface MyGenericType<out T> {
string key();
}
interface IDerivedContainableObject : IContainableObject {
}
class Program {
private static Dictionary<String, MyGenericType<IContainableObject>> m_elementDictionary = new Dictionary<String, MyGenericType<IContainableObject>>();
public static void Register<T>(T objectToRegister)
where T : MyGenericType<IContainableObject> {
m_elementDictionary[objectToRegister.key()] = objectToRegister;
}
static void Main(string[] args) {
MyGenericType<IDerivedContainableObject> x = null;
MyGenericType<IContainableObject> y = x;
Register(y);
}
}
(请注意,MyGenericType 现在是一个接口)
这不起作用的原因是 C# 标准将泛型定义为不变的 [1]。这意味着如果我们有一个基类(或接口)B
和一个派生类D
那么我们可以说以下内容:
-
D
是B
的一个亚型 -
D[]
是B[]
的一个亚型 - 但是
G<D>
不是G<B>
的子类型,其中G
是任何泛型类型。
编译器将尝试在两种不变类型G<D>
和G<B>
之间进行隐式转换,并且肯定会失败,因为那里没有定义转换。
这恰好也是您的情况,因为您正在尝试从MyGenericType<some_object>
转换为MyGenericType<IContainableObject>
。
语义上讲,这实际上是有意义的,因为您并没有真正从派生类转换为基类,而是在两个泛型类型之间进行更多转换。
列表可以注意到相同的行为:
List<D> base_list = new List<D>(); //this will give an error message
我不知道您提供相关建议的具体要求,但在大多数情况下,我很可能会将此实现隐藏在接口下并将这些接口存储在字典中(您已经提到过)。这也将提供自由解耦。
引用
[1] CLR 中的泛型类型参数方差
我不确定这会清楚,但它的工作:
public interface IContainableObject
{
}
public interface IMyGenericType<in T>
where T:IContainableObject
{
string key{get;set;}
}
public abstract class MyGenericType<T> : IMyGenericType<IContainableObject>
where T : IContainableObject
{
public string key{get;set;}
}
public class MyTypedClass:MyGenericType<DerivedContainableObject>
{
}
public class DerivedContainableObject:IContainableObject
{
}
public class Registry
{
private Dictionary<String, IMyGenericType<IContainableObject>> m_elementDictionary = new Dictionary<String, IMyGenericType<IContainableObject>>();
public void Register<T>(MyGenericType<T> objectToRegister)
where T:IContainableObject
{
m_elementDictionary[objectToRegister.key] = objectToRegister; //This now work
}
public void ExampleMethod()
{
Register<DerivedContainableObject>(new MyTypedClass());
}
}