7

我有一个定义如下的接口:

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

我有 IEnumerable 列表 a 和 b 由 tblTestA 和 tblTestB 的数据库记录填充

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)

有什么我做错了吗?谢谢你的帮助

4

4 回答 4

9

由于泛型协方差,这在 C# 4 中有效。与以前版本的 C# 不同,有一个从IEnumerable<tblTestA>到的转换IEnumerable<TestInterface>

该功能已在 v2 的 CLR 中,但仅在 C# 4 中公开(并且框架类型在 .NET 4 之前也没有利用它)。它适用于通用接口和委托(不是类),并且仅适用于引用类型(例如,没有从IEnumerable<int>to的转换IEnumerable<object>。)它也只适用于有意义的地方 -IEnumerable<T>是协变的,因为对象只从 API“出来”,而IList<T>不变的,因为您也可以使用该 API 添加值。

还支持通用逆变,在另一个方向上工作 - 例如,您可以从转换IComparer<object>IComparer<string>.

如果您不使用 C# 4,那么 Tim 的使用建议Enumerable.Cast<T>是一个很好的建议 - 您会损失一点效率,但它会起作用。

如果您想了解更多关于泛型变异的信息,Eric Lippert 有一系列关于它的博文,我在 NDC 2010 上发表了关于它的演讲,您可以在NDC 视频页面上观看。

于 2010-07-14T09:49:45.780 回答
6

你没有做错任何事:List<TestInterface>.AddRange期望一个IEnumerable<TestInterface>. 它不接受IEnumerable<tblTestA>or IEnumerable<tblTestB>

你的foreach循环工作。或者,您可以使用Cast来更改类型:

List<TestInterface> list = new List<TestInterface>();
list.AddRange(a.Cast<TestInterface>());
list.AddRange(b.Cast<TestInterface>());
于 2010-07-14T09:47:28.607 回答
1

AddRange 需要一个接口对象列表,并且您的“a”和“b”变量被定义为派生类对象的列表。显然,.NET 可以在逻辑上进行跳转并将它们视为接口对象的列表似乎是合理的,因为它们确实实现了接口,该逻辑只是没有内置到 .NET 到 3.5。

但是,此功能(称为“协方差”)已添加到 .NET 4.0,但在升级到该功能之前,您将陷入循环,或者尝试调用 ToArray(),然后将结果转换为 TaskInterface[] ,或者可能是一个 LINQ 查询来区分每个项目并创建一个新列表等。

于 2010-07-14T09:56:19.797 回答
0

aandb是类型IEnumerable<tblTestA>and IEnumerable<tblTestB>
Whilelist.AddRange要求参数是类型IEnumerable<TestInterface>

于 2010-07-14T09:48:34.783 回答