如何将通用 List<T> 转换为基于接口的 List<T>
Posted
技术标签:
【中文标题】如何将通用 List<T> 转换为基于接口的 List<T>【英文标题】:How to convert a generic List<T> to an Interface based List<T> 【发布时间】:2011-01-17 08:47:15 【问题描述】:我确定我遗漏了一些简单的东西,但是我正在尝试将所有实现接口的对象的强类型列表转换为该接口类型的列表。
下面是一个演示错误的示例:
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;
【问题讨论】:
注意:问题中的代码被表述为演员表(即同一对象的不同视图)。下面给出的不同解决方法复制列表 - 创建一个新列表并将每个 Shape 元素添加到新列表中 - 给出不同的对象,而不是同一对象的不同视图。 【参考方案1】:不要像那样投射,试试:
allShapes = cubes.Cast<Shape>().ToList();
为此,您需要 .NET 3.5。我相信可以在 System.Linq 中找到 Cast 扩展方法。
【讨论】:
注意,这会复制列表(尽管如果它包含引用引用而不是对象将被复制)。这与转换列表对象本身不同。 这与您在 .NET 3.5 中所能获得的一样接近。无法按要求转换列表对象。【参考方案2】:你不能。因为List<T>
和ILIst<T>
只支持不变类型参数。这归结为T
同时用于输入和输出参数(例如返回值)。否则你可能会破坏类型安全。
其他接口(例如IEntumerable<T>
)确实允许一些差异。
请参阅 Eric Lippert 的博客“Fabulous Adventures In Coding”,了解对方差和协方差的讨论。特别是“Covariance and Contravariance”标签。
编辑,刚刚添加到“C#常见问题”博客:Covariance and Contravariance FAQ
【讨论】:
【参考方案3】:您所指的内容称为泛型协方差,c# 3 不支持它。但是,c# 4 (.NET 4 / VS 2010) 支持它,您可以在此处阅读更多信息:
Variance in Generic Interfaces
话虽如此,IList<T>
不是协变的(因为它既接受又公开T
)。另一方面,IEnumerable<T>
是协变的(因为它不接受 T
)。
【讨论】:
【参考方案4】:您不能这样做,因为通过这种方式转换可能会失去所有类型安全性。例如,将List<Shape>
转换为List<object>
将导致any 类型的对象可以添加到列表中,这将是完全不一致的。
【讨论】:
【参考方案5】:你也可以这样做:
allShapes = cubes.ConvertAll(x => (Shape)x);
或者,如果您在 .NET 2.0 中执行此操作:
allShapes = cubes.ConvertAll<Shape>(delegate(Cube c)
return (Shape)c;
);
【讨论】:
【参考方案6】:您不能在类型列表之间进行转换,即使类型本身是可转换的。您将需要创建一个新列表并填充它,方法是迭代原始列表或使用 ConvertAll。示例代码见http://www.dev102.com/2008/05/15/how-to-convert-listt1-to-listt2/。
【讨论】:
【参考方案7】:如果您将 allShapes 定义为 IEnumerable,则此方法有效
在 C# 4.0 中,您可以简单地分配 allshapes=cubes
对于 C# 3.5,您可以使用 allShapes = cubes.Select(c=>((Shape)c));
但无论如何你都需要使用 IEnumerable 而不是 List
【讨论】:
以上是关于如何将通用 List<T> 转换为基于接口的 List<T>的主要内容,如果未能解决你的问题,请参考以下文章
如何在 C# 中将 IEnumerable<T> 转换为 List<T>?