views:

6019

answers:

2

Hi, all

I'm trying to combine a number of List<T> where T:IGetTime (i.e T will always have method getTime()).

Then I'm trying order the items by the DateTime that getTime() returns.

My LINQ looks like this:

public static List<T> Combine(List<T> one, List<T> two, List<T> three)
    {
        var result = from IGetTime item in one
                     from IGetTime item2 in two
                     from IGetTime item3 in three
                     select new{ item, item2, item3 };

        return result.ToList();
    }

I've yet to add an orderby. Which should look somthing like this:

var thestream = from T item in this
                orderby item.getTime() descending
                select item;

Is there anyway to both combine and order the final list????

Thanks in advance,

Roberto

+12  A: 

You've got three lists here - what exactly are you trying to order by?

Your cast is incorrect too - the result will be an IEnumerable<X> where X is an anonymous type with three Ts in.

If the idea is really to concatenate the three lists and then sort, you want:

return one.Concat(two)
          .Concat(three)
          .OrderByDescending(x => x.GetTime())
          .ToList();
Jon Skeet
Does C# know that x will have a GetTime method (or cast it toa IGetTime) simply due to a where T: IGetTime? If not the Lambda needs adjustment to x => ((IGetTime)x).GetTime()
AnthonyWJones
C~ does know. T is explicitly declated inthe clas decleration: public class TimeStream<T> : List<T> where T : IGetTime
Roberto Bonini
Wow it does work without the cast, can't help shake the feeling that this is taking C# magix pixie dust a little too far.
AnthonyWJones
@Roberto: There is still magic happening, it is silently casting x to IGetTime even though the type of T is something else. Its doing this by checking each interface in the restriction list looking for a matching signature of GetTime().
AnthonyWJones
Thanks alot. This one had me stumped.
Roberto Bonini
@AnthonyWJones: Much of the benefit of generics is to replace run-time casting with compile-time type checking. The types are all there, specified by the programmer. The compiler doesn't have to work too hard to check that these instances have that method.
David B
+3  A: 

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.

David B