Covariance/Contravariance不应该在C#4.5中允许这样吗?

Dyl*_*ith 2 c# covariance contravariance c#-4.0

private Dictionary<Type, List<IDataTransferObject>> dataStore = new Dictionary<Type, List<IDataTransferObject>>();

public void Insert<T>(T dto) where T : IDataTransferObject
{
    if (!dataStore.ContainsKey(typeof(T)))
    {
        dataStore.Add(typeof(T), new List<T>());
    }

    dataStore[typeof(T)].Add(dto);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码给出了dataStore.Add行的编译错误,因为它不喜欢我尝试将a分配List<T>给a List<IDataTransferObject>.由于我的方法将T限制为只有IDataTransferObject,因此.Net 4中的协方差/逆变量不应该允许这个代码吗?

我知道我可以改变它来做新的List<IDataTransferObject>它会起作用,但我很好奇为什么原始代码不起作用.

Chr*_*air 6

很确定a List<SubClass>不是协变的List<BaseClass>.IEnumerable<T>也许,但不是List,因为你可以自由地添加一个非T(但仍然IDataTransferObjects)会抛出运行时异常,因此它在编译时被捕获.

虽然您的代码在运行时可能是安全的(因为您按类型使用键),但编译器不知道这一点.

List<Animal> animalList = new List<Animal>();
animalList.Add(new Dog()); //ok!

List<Cat> catList = new List<Cat>();
animalList = catList; //Compiler error: not allowed, but it's what you're trying to do
animalList.Add(new Dog()) //Bad stuff! Trying to add a Dog to a List<Cat>
Run Code Online (Sandbox Code Playgroud)

你正在尝试将其视为IEnumerable<IDataTransferObject>无法通过代码修改的内容(除非你先将它强制转换,如果你使用了错误的类型,它会通过/失败).但List绝对可以通过编译时代码进行更改.

编辑:如果你不介意转换,并且真的想要一个List<T>(所以你的调用代码是类型安全的,T一旦检索不添加非对象)你可能会这样做:

private Dictionary<Type, object> dataStore = new Dictionary<Type, object>();

public void Insert<T>(T dto) where T : IDataTransferObject
{
    object data;
    if (!dataStore.TryGetValue(typeof(T), out data))
    {
        var typedData = new List<T>();
        dataStore.Add(typeof(T), typedData);
        typedData.Add(dto);
    }
    else
    {
        ((List<T>)data).Add(dto);
    }
}


//you didn't provide a "getter" in your sample, so here's a basic one
public List<T> Get<T>() where T : IDataTransferObject
{
    object data;
    dataStore.TryGetValue(typeof(T), out data);
    return (List<T>)data;
}
Run Code Online (Sandbox Code Playgroud)

调用代码如下:

Insert(new PersonDTO());
Insert(new OrderDTO());
Insert(new PersonDTO());

List<PersonDTO> persons = Get<PersonDTO>();
List<OrderDTO> orders = Get<OrderDTO>();

Console.WriteLine(persons.Count); //2
Console.WriteLine(orders.Count); //1
Run Code Online (Sandbox Code Playgroud)

因此,从外部来看,所有API使用都是类型安全的.它不是orders一个List<IDataTransferObject>(这意味着你可以添加非OrderDTO对象),而是强类型的,不能混合和匹配.

当然,在这一点上,没有必要限制IDataTransferObject,但这取决于您和您的API /设计/用途.