存储通用对象的列表/字典

时间:2015-01-21 07:58:21

标签: c# list generics dictionary covariance

我想知道是否有存储在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,因为我们知道我们使用泛型类型接收的参数实际上是MyGenericType<IContainableObject>,因为在哪里条件。

请注意:

  1. 我知道我可以在MyGenericType<IContainableObject>商店的界面上添加一个字典。这是主题。
  2. 我知道我可以有一个MyGenericType<IContainableObject>参数,这也是重点。
  3. 我更关注协方差/逆变在这里是否有帮助?

3 个答案:

答案 0 :(得分:1)

你应该表达这样的条件:

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现在是一个接口)

答案 1 :(得分:0)

这不起作用的原因是因为C#标准将泛型定义为不变 [1]。这意味着如果我们有一个基类(或接口)B和一个派生类D,那么我们可以说如下:

  • DB
  • 的子类型
  • 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] Generic type parameter variance in the CLR

答案 2 :(得分:0)

我不确定这一点是否清楚,但它的工作原理是:

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());
    }
}