Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic list of lists, converting List<List<T>> to IList<IList<T>>

We're using a class library that performs calculations on 3D measurement data, it exposes a method:

MeasurementResults Calculate(IList<IList<Measurement>> data)

I would like to allow calling this method with any indexable list of lists (of Measurement of course), for example both:

Measurement[][] array;
List<List<Measurement>> list;

Calling the method using the array works fine, which is a bit strange. Is there some compiler trick at work here? Trying to call with the List gives the familiar error:

cannot convert from 'List<List<Measurement>>' to 'IList<IList<Measurement>>'

So, I have written a facade class (containing some other things as well), with a method that splits the generic definition between the argument and method and converts to the IList type if necessary:

MeasurementResults Calculate<T>(IList<T> data) where T : IList<Measurement>
{
  IList<IList<Measurement>> converted = data as IList<IList<Measurement>>;
  if(converted == null)
    converted = data.Select(o => o as IList<Measurement>).ToList();
  return Calculate(converted);
}

Is this a good way to solve the problem, or do you have a better idea?

Also, while testing different solutions to the problem, I found out that if the class library method had been declared with IEnumerable instead of IList, it is ok to call the method using both the array and the List:

MeasurementResults Calculate(IEnumerable<IEnumerable<Measurement>> data)

I suspect that there is some compiler trick at work again, I wonder why they haven't made IList work with List while they were at it?

like image 930
Anlo Avatar asked Apr 19 '12 14:04

Anlo


1 Answers

It's okay to do this with IEnumerable<T>, because that is covariant in T. It wouldn't be safe to do that with IList<T>, as that is used for both "input" and "output".

In particular, consider:

List<List<Foo>> foo = new List<List<Foo>>();
List<IList<Foo>> bar = foo;
bar.Add(new Foo[5]); // Arrays implement IList<T>

// Eh?
List<Foo> firstList = foo[0];

See MSDN for more information about generic covariance and contravariance.

like image 68
Jon Skeet Avatar answered Sep 19 '22 10:09

Jon Skeet