2012-10-05 28 views
2
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); 
} 

上面的代码给我一个dataStore.Add行的编译错误,因为它不喜欢我试图将List<T>分配给List<IDataTransferObject>。由于我的方法将T限制为只有IDataTransferObject,因此.Net 4中的协方差/相反变量不应该允许此代码?不应该在C#4.5中使用协变/反变量吗?

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

+4

[Skeet to the rescue。](http://stackoverflow.com/questions/2033912/c-sharp-variance-problem-assigning-listderived-as-listbase) – Sumo

+0

也许你对版本有点困惑号码?如果您指的是.NET 4.5(C#5),则有新的['IReadOnlyList '](http://msdn.microsoft.com/zh-cn/library/hh192385.aspx)接口,它是**协变**在它的'T'中。请注意,在仅限.NET的接口(和委托类型)中,不是类/结构体可以是协变的。 IReadOnlyList <>'可以变为协变的原因是它只包含了从底层列表中读取的成员(no _write_)。然而,'IReadOnlyList <>'不能帮助你解决你的“不可能”问题,所以.NET 4.5在这里毫无帮助。 –

回答

6

很确定一个List<SubClass>是不协变到List<BaseClass>IEnumerable<T>也许,但不是列表,因为你可以自由地添加一个非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> 

你在做什么,如果你试图把它当作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; 
} 

调用代码如下:

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 
从外面

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

当然在这一点上,没有必要约束到IDataTransferObject,但这取决于你和你的API /设计/使用。

相关问题