Adding a subclass collection using AddRange
If I have these two classes:
class A {}
class B : A {}
and I am doing List <A> but I want to add list <B> to it by calling List <A> .AddRange (List <B>) but the compiler refuses:
Argument '1': cannot convert from 'System.Collections.Generic.List<A>'
to 'System.Collections.Generic.IEnumerable<B>
which I fully understand because IEnumerable <B> does not inherit from IEnumerable <A>, its generic type is inheritance.
My solution is to enumerate through List <B> and add elements individually, because List <A> .Add (A element) will work with B elements:
foreach(B item in listOfBItems)
{
listOfAItems.Add(item);
}
However, this is rather not expressive, because I just want AddRange.
I could use
List<B>.ConvertAll<A>(delegate(B item) {return (A)item;});
but this is unnecessarily confusing and wrong because I am not converting, I am casting.
Question . If I were to write my own List-like collection, what method would I add to it that would allow me to copy collection B to collection A as a one-liner similar to List <A> .AddRange (List <B>) and keep the maximum type safety. (And to the max, I mean that the argument is a collection and type integrity check.)
source to share
Indeed, generic types are not options right now. In C # 4.0 IEnumerable <B
> will be convertible to IEnumerable <A
> if B can be converted to via reference conversion. For details on the design of this feature, see
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
source to share
This fails unsuccessfully because generics in .net do not support covariance.
You can make a small helper method or class to solve this problem.
If you implement your own list class, you can add covariance using an additional generic parameter:
class MyList<T> {
void AddRange<U>(IEnumerable<U> items) where U: T {
foreach (U item in items) {
Add(item);
}
}
}
source to share
If you find yourself in a situation where generic types are not variants, the following extension method can make your life easier:
public static void AddRange<TList,TOther>(this List<TList> list, IEnumerable<TOther> collection) where TOther: TList {
foreach(TOther e in collection) {
list.Add(e);
}
}
Instead of retrieving from List<T>
or having this method in some utility class, using it as an extension method makes it easier to use. You can also profit from the withdrawal, so this previously invalid call will become valid without any changes:
List<Animal> animals;
List<Dog> dogs;
animals.AddRange(dogs);
source to share
The only thing I can think of is
public class MyList<T> : List<T>
{
public void AddRange<Tother>(IEnumerable<Tother> col)
where Tother: T
{
foreach (Tother item in col)
{
this.Add(item);
}
}
}
Calling means using MyList <A> .AddRange <B> (MyList <B>). It fails if the argument is not enumerable, or if type inheritance doesn't work, so it meets my requirement for maximum security.
source to share