Pretty precisely, a List<SubClass> not covariant to a List<BaseClass> . IEnumerable<T> may be, but not List, since you can freely add not T (but still IDataTransferObjects ), which would throw an exception at runtime to IDataTransferObjects it at compile time.
Although your code may be safe at runtime (since you use keys by type), the compiler does not know this.
List<Animal> animalList = new List<Animal>(); animalList.Add(new Dog());
What you do will work if you try to treat it as an IEnumerable<IDataTransferObject> , since they cannot be changed using the code (unless you say it first, at what point it will go through / crash if you use bad type of). But List can definitely be changed by compile-time code.
EDIT: If you don't mind casting and really want a List<T> (so your calling code is typical and doesn't add non- T objects after extraction), you can 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); } }
The call code is as follows:
Insert(new PersonDTO()); Insert(new OrderDTO()); Insert(new PersonDTO()); List<PersonDTO> persons = Get<PersonDTO>(); List<OrderDTO> orders = Get<OrderDTO>(); Console.WriteLine(persons.Count);
Thus, from outside, all use of the API is typical. Instead of orders is List<IDataTransferObject> (which means that you can add objects not OrderDTO ), it is strongly typed and cannot be mixed and matched.
Of course, at the moment there is no real need to restrict IDataTransferObject , but it depends on you and your API / design / use.
Chris sinclair
source share