协方差/协方差不应该在C#4.5中允许吗? [英] Shouldn't Covariance/Contravariance allow this in C# 4.5?
问题描述
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中的协方差/相反方的东西不应该允许此代码?
The above code gives me a compile error on the dataStore.Add line because it doesn't like me trying to assign a List<T>
to a List<IDataTransferObject>
. Since my method restricts T to only IDataTransferObject's shouldn't the covariance/contravariance stuff in .Net 4 allow this code?
我知道我可以将其更改为执行新的 List< IDataTransferObject>
,它可以工作,但是我很好奇为什么原始代码不起作用。
I know I can change it to do new List<IDataTransferObject>
and it will work, but I'm curious why the original code doesn't work.
推荐答案
很确定 List< SubClass>
与 List< BaseClass>
。 IEnumerable< T>
可能是,但不是列表,因为您可以自由添加非 T
(但仍 IDataTransferObjects
)会抛出运行时异常,因此在编译时会被捕获。
Pretty sure a List<SubClass>
isn't covariant to List<BaseClass>
. IEnumerable<T>
maybe, but not List as you can freely add a non-T
(but still IDataTransferObjects
) which would throw a runtime exception so it's caught at compile time.
虽然您的代码在运行时可能是安全的(如
While your code might be safe at runtime (as you use keys by type), the compiler doesn't know this.
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
肯定可以通过编译时代码来更改。
What you're doing would work if you were trying to treat it as IEnumerable<IDataTransferObject>
as those cannot by modified by code (unless you cast it first at which point it would pass/fail if you use a bad type). But List
can definitely be altered by compile-time code.
编辑:如果您不介意进行强制转换,并且真的想要一个 List< T>
(因此您的调用代码是类型安全的,一旦检索就不添加非 T
对象),您可以执行以下操作:
If you don't mind casting, and really want a List<T>
(so your calling code is typesafe and not adding non-T
objects once retrieved) you might do something like this:
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
对象),它是强类型的,不能混合和匹配。
So from the outside, all API usage is typesafe. Instead of orders
being a List<IDataTransferObject>
(which means you can add non-OrderDTO
objects), it's strongly typed and cannot be mixed and matched.
当然,在这一点上,实际上并不需要限制为 IDataTransferObject
,但这取决于您和您的API /设计/使用情况。
Of course at this point, there's no real need to constrain to IDataTransferObject
, but that's up to you and your API/design/usage.
这篇关于协方差/协方差不应该在C#4.5中允许吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!