通用列表列表,转换List <list <t>&gt;到IList <ilist <t>&gt; </ilist <t> </list <t>

时间:2012-04-19 14:12:21

标签: c# .net generics ienumerable ilist

我们正在使用一个对3D测量数据进行计算的类库,它公开了一种方法:

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

我想允许使用任何可索引的列表列表(当然是Measurement)调用此方法,例如:

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

使用数组调用方法工作正常,这有点奇怪。这里有一些编译技巧吗?尝试使用List进行调用会产生熟悉的错误:

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

所以,我编写了一个facade类(包含其他一些东西),并使用一个方法在参数和方法之间拆分泛型定义,并在必要时转换为IList类型:

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);
}

这是解决问题的好方法,还是你有更好的想法?

此外,在测试问题的不同解决方案时,我发现如果类库方法已使用IEnumerable而不是IList声明,则可以使用两个数组调用该方法和List

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

我怀疑有一些编译器技巧再次起作用,我想知道为什么他们在IList处于List时没有使用它们?

3 个答案:

答案 0 :(得分:6)

可以使用IEnumerable<T>执行此操作,因为T 中的协变。使用IList<T>进行此操作是不安全的,因为它用于“输入”和“输出”。

特别要考虑:

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];

有关generic covariance and contravariance的详细信息,请参阅MSDN。

答案 1 :(得分:3)

假设我们有一个方法

void Bar(IList<IList<int>> foo)

Bar内,应该完全可以将int[]添加到foo - 毕竟int[]实现IList<int>,不是吗?

但如果我们使用Bar调用List<List<int>>,我们现在会尝试将int[]添加到只接受List<int>的内容中!这将是。所以编译器不允许你这样做。

  

此外,在测试问题的不同解决方案时,我发现如果类库方法已使用IEnumerable而不是IList声明,则可以使用两个数组调用该方法和List

确实,因为如果行为合同只是说“我可以输出 int s”,那么什么都不会出错。进一步研究的关键术语是协方差逆变,没有一个 * 能记住哪个是哪个。

在您的特定情况下,如果Calculate只是正在阅读其输入,将其更改为使用IEnumerable绝对是正确的做法 - 它都允许您通过任何符合条件的对象,并进一步传达给任何阅读签名的人,该方法有意设计为仅消费,而不是改变其输入。


* 好吧,大多数没有人

答案 2 :(得分:1)

我实施的一些内容会改变。首先,它可能会在原始对象中的对象中插入空值。我个人更喜欢扔它。

MeasurementResults Calculate<T>(IList<T> data) where T : IList<Measurement>
{
    return Calculate(data as IList<IList<Measurement>>
                     ?? data.Cast<IList<Measurement>>().ToList());
}

一个简短的例子就像我个人在你的情况下做的那样,虽然我怀疑我实际上会实现这个方法。界面是这样编写的原因(我希望),我会尝试在我的实现中使用这些知识,而不是在可能的情况下与之斗争。如果您使用代码(或类似的东西),对方法列表所做的任何更改都不会反映在原始列表中。这是一个传递给该方法的新列表,由于签名要求IList,因此可以进行更改。

除了可能在列表中没有空值而不是对象之外我还改为使用cast方法,因为那基本上就是你想要完成的。 (Cast不允许使用自定义转换运算符。)