Here's how I'd do it:
public static List<T> Combine<T, TKey>
(
Func<T, TKey> orderingFunction,
params IEnumerable<T>[] theParams
)
where TKey : IComparable
{
return theParams
.SelectMany(x => x)
.OrderBy(orderingFunction)
.ToList();
}
Here's some test code:
public static void Test1()
{
List<int> a = new List<int>() { 1, 2, 3 };
List<int> b = new List<int>() { 3, 2, 1 };
List<int> c = CombineLists.Combine(i => i, a, b);
c.ForEach(i => Console.WriteLine(i));
}
Here's the story:
public static List<T> Combine<T, TKey>
(
Func<T, TKey> orderingFunction,
params IEnumerable<T>[] theParams
)
where TKey : IComparable
This signature declares a method that takes two type parameters, some ordering function and a bunch of collections. Notice that the types T and TKey are not specified explicitly in the test call. The compiler has a little puzzle to figure out what these types are. First it inspects the IEnumerables that it has been given (a and b) and discovers they are IEnumerable of int. Aha, T must be int. Then it inspects the ordering function and sees that it returns the same type that it is passed. Aha, TKey must be int.
The where is kind of funny. OrderBy should check (at compile time) that TKey is IComparable. It only checks at runtime, so we get to do the compile time check in our code.
params takes as many parameters as you like and refers to them be creating an array. Who wants to be limited to three lists?
return theParams
.SelectMany(x => x)
.OrderBy(orderingFunction)
.ToList();
SelectMany unpacks a collection of collections. This is a way to flatten one level of hierarchy.
OrderBy takes some function that projects each element to an orderable value, and orders the elements according to those values.
ToList enumerates the query and returns the list. No surprises there.