我的界面定义如下:
public interface TestInterface{
int id { get; set; }
}
实现该接口的两个Linq-to-SQL类:
public class tblTestA : TestInterface{
public int id { get; set; }
}
public class tblTestB : TestInterface{
public int id { get; set; }
}
我有来自tblTestA和tblTestB的数据库记录填充的IEnumerable列表a和b
IEnumerable<tblTestA> a = db.tblTestAs.AsEnumerable();
IEnumerable<tblTestB> b = db.tblTestBs.AsEnumerable();
但是,不允许以下内容:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a);
list.AddRange(b);
我必须做如下:
foreach(tblTestA item in a)
list.Add(item)
foreach(tblTestB item in b)
list.Add(item)
我做错了吗?谢谢你的帮助
答案 0 :(得分:8)
由于通用协方差,这适用于C#4。与以前版本的C#不同,有从IEnumerable<tblTestA>
到IEnumerable<TestInterface>
的转换。
该功能已经在v2中的CLR中,但它只在C#4中公开(并且框架类型在.NET 4之前也没有利用它)。 仅适用于通用接口和委托(不是类),仅适用于引用类型(例如,没有从IEnumerable<int>
到IEnumerable<object>
的转换。)它也只适用于这是有道理的 - IEnumerable<T>
是协变的,因为对象只是“超出”API,而IList<T>
是不变,因为你也可以使用该API添加值。
也支持通用逆转,在另一个方向工作 - 例如,您可以从IComparer<object>
转换为IComparer<string>
。
如果您没有使用C#4,那么Tim建议使用Enumerable.Cast<T>
是一个很好的建议 - 您会失去一点效率,但它会起作用。
如果您想了解有关泛型差异的更多信息,Eric Lippert有一个long series of blog posts about it,我在NDC 2010上发表了一篇关于它的讨论,您可以在NDC video page上观看。
答案 1 :(得分:6)
你没有做错任何事:List<TestInterface>.AddRange
期待IEnumerable<TestInterface>
。它不接受IEnumerable<tblTestA>
或IEnumerable<tblTestB>
。
您的foreach
循环有效。或者,您可以使用Cast
更改类型:
List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
答案 2 :(得分:1)
AddRange需要一个接口对象列表,而你的“a”和“b”变量被定义为派生类对象的列表。显然,.NET可以在逻辑上进行跳转并将它们视为接口对象的列表似乎是合理的,因为它们确实实现了接口,而逻辑只是通过3.5构建到.NET中。
但是,这种能力(称为“协方差”)已被添加到.NET 4.0中,但在升级到此之前,您将陷入循环,或者尝试调用ToArray()然后将结果转换为TaskInterface [],或者可能是一个LINQ查询来判断每个项目并创建一个新列表等等。
答案 3 :(得分:0)
a
和b
的类型为IEnumerable<tblTestA>
和IEnumerable<tblTestB>
虽然list.AddRange
要求参数的类型为IEnumerable<TestInterface>