C# offers a powerful API for working with collections and especially LINQ offers lots of functional goodies to work with them. Among them is also the Concat()
-method which allows to concatenate two IEnumerables
.
We recently had the use-case of concatenating two collections with elements of a common super-type:
class Animal {}
class Cat : Animal {}
class Dog : Animal {}
public IEnumerable<Animal> combineAnimals(IEnumerable<Cat> cats, IEnumerable<Dog> dogs)
{
// XXX: This does not work because Concat is invariant!!!
return cats.Concat(dogs);
}
The above example does not work because concat requires both sequences to have the same type and returns a combined sequences of this type. If we do not care about the specifities of the subclasses be can build a Concatenate()
-method ourselves which make the whole thing possible because instances of both subclasses can be put into a collection of their common parent class.
private static IEnumerable<TResult> Concatenate<TResult, TFirst, TSecond>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second)
where TFirst: TResult where TSecond : TResult
{
IList<TResult> result = new List<TResult>();
foreach (var f in first)
{
result.Add(f);
}
foreach (var s in second)
{
result.Add(s);
}
return result;
}
The above method is a bit clunky to call but works as intended:
public IEnumerable<Animal> combineAnimals(IEnumerable<Cat> cats, IEnumerable<Dog> dogs)
{
// Works great!
return cats.Concatenate<Animal, Cat, Dog>(dogs);
}
A variant of the above is a Concatenate()
-method can be useful if you use a collection of the parent class to collect instances of subclass collections:
private static IEnumerable<TResult> Concatenate<TResult, TIn>(
this IEnumerable<TResult> first,
IEnumerable<TIn> devs)
where TIn : TResult
{
IList<TResult> result = first.ToList();
foreach (var dev in devs)
{
result.Add(dev);
}
return result;
}
public IEnumerable<Animal> combineAnimals(IEnumerable<Cat> cats, IEnumerable<Dog> dogs)
{
IEnumerable<Animal> result = new List<Animal>();
result = result.Concatenate(cats);
return result.Concatenate(dogs);
}
Maybe the above examples can serve as an inspiration for more utility methods that may improve working with collections in C#.