Shouldn't Covariance/Contravariance allow this in C# 4.5?

左心房为你撑大大i 提交于 2019-12-06 06:04:47

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>

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.

EDIT: 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;
}

Calling code is like:

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

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!