如何将一个通用的List<T>转换为基于接口的List<T>?

7

我相信我错过了一些简单的东西,但是我正在尝试将一个强类型对象列表转换为一个该接口类型的列表。

下面是一个示例,用于演示错误:

public void ExampleCode(){
    List<Cube> cubes = new List<Cube>();
    List<Shape> allShapes;
    allShapes = cubes;//Syntax Error
    allShapes = (List<Shape>)cubes;//Syntax Error  
}

public class Cube : Shape
{
    public int ID { get; set; }
    public int Sides { get; set; }
}

public interface Shape
{
  int ID { get; set; }
  int Sides { get; set; }
}

注意:问题中的代码被表达为一个强制类型转换(即相同对象的不同视图)。下面给出的不同解决方案都是复制列表 - 创建了一个新列表,并将每个形状元素添加到新列表中 - 这会产生一个不同的对象,而不是相同对象的不同视图。 - Binary Worrier
7个回答

13

不要像那样进行强制类型转换,尝试使用以下方法:

allShapes = cubes.Cast<Shape>().ToList();

你需要使用 .NET 3.5 版本。我相信 Cast 扩展方法可以在 System.Linq 中找到。


2
注意,这将复制列表(即使它包含引用,也只会复制引用而不是对象)。这与将列表对象本身强制转换不同。 - Richard
1
这是在.NET 3.5中最接近的方法。没有办法将列表对象转换为所需类型。 - Lasse V. Karlsen

6
你做不到。因为 List<T>ILIst<T> 只支持不变的类型参数。这是因为 T 同时用于输入和输出参数(例如返回值)。否则,您可能会破坏类型安全性。
其他接口(例如 IEntumerable<T>)允许一些方差。
请参阅 Eric Lippert 的博客 "Fabulous Adventures In Coding",以了解逆变性和协变性的讨论。具体而言,标签为 "Covariance and Contravariance"。
编辑,刚刚添加到 "C# 常见问题" 博客中:Covariance and Contravariance FAQ

4
你所提到的被称为通用协变性,c# 3 不支持。然而,它在 c# 4 (.NET 4 / VS 2010) 中得到了支持,你可以在这里阅读更多相关内容:
Variance in Generic Interfaces
话虽如此,IList 不是协变的(因为它既接受又暴露 T)。另一方面,IEnumerable 是协变的(因为它不接受 T)。

2

您不能这样做,因为通过这种方式转换类型可能会导致失去所有类型安全性。例如,将List<Shape>强制转换为List<object>将导致可以向列表中添加任何类型的对象,这将是彻头彻尾的不一致。


0

你也可以这样做:

allShapes = cubes.ConvertAll(x => (Shape)x);

如果您正在使用.NET 2.0,则可以这样做:

allShapes = cubes.ConvertAll<Shape>(delegate(Cube c){
    return (Shape)c;
});

0

0

如果您将所有形状定义为IEnumerable,则此方法可行。

在C#4.0中,您可以简单地将allshapes = cubes赋值。

对于C#3.5,您可以使用allShapes = cubes.Select(c => ((Shape)c))。

但无论如何,您需要使用IEnumerable而不是List。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接