- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
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 中的协变/逆变内容不应该允许此代码吗?
我知道我可以将其更改为新的 List<IDataTransferObject>
它会起作用,但我很好奇为什么原始代码不起作用。
最佳答案
很确定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/设计/用法。
关于c# - C# 4.5 中协变/逆变不应该允许这样做吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12739040/
在我的设置中,我试图有一个界面 Table继承自 Map (因为它主要用作 map 的包装器)。两个类继承自 Table - 本地和全局。全局的将有一个可变的映射,而本地的将有一个只有本地条目的映射。
Rust Nomicon 有 an entire section on variance除了关于 Box 的这一小节,我或多或少地理解了这一点和 Vec在 T 上(共同)变体. Box and Vec
我是一名优秀的程序员,十分优秀!