List<T>的强制类型转换 - 协变/逆变问题

15

考虑以下类型:

public interface IMyClass { }
public class MyClass : IMyClass { }

我想知道如何将一个List<MyClass>转换成一个List<IMyClass>? 我并不完全清楚协变性/逆变性的话题,但我知道因为这个原因我不能直接强制转换List。

我只能想到这个平凡无奇的解决方案,没有任何优雅之处,而且浪费资源:

...
public List<IMyClass> ConvertItems(List<MyClass> input)
{
   var result = new List<IMyClass>(input.Count);
   foreach (var item in input)
   {
       result.Add(item);
   }
   return result;
}
....

有什么更加优雅/高效的解决方案吗?

(请注意,我需要 .NET 2.0 的解决方案,但为了完整起见,我也很乐意看到使用更新框架版本的更加优雅的解决方案。)

3个回答

24

最简单的方法可能是使用ConvertAll函数:

List<IMyClass> converted = original.ConvertAll<IMyClass>(x => x);

即使您使用的是 .NET 2 版本,如果您使用的是 VS2008 或更高版本,则可以使用 lambda 语法。否则,您总是可以使用匿名方法:

List<IMyClass> converted = original.ConvertAll<IMyClass>(
    delegate (MyClass x) { return x; });

在.NET 3.5中,您可以使用CastOfType甚至只是Select来使用LINQ:
var converted = original.Cast<IMyClass>().ToList();
var converted = original.OfType<IMyClass>().ToList();
var converted = original.Select(x => (IMyClass) x).ToList();

在 .NET 4.0 中,由于 IEnumerable<T> 的协变性,您可以直接使用 ToList 而不需要中间转换。
var converted = original.ToList<IMyClass>();

这是协变性的一个巧妙运用,非常棒。 - Ani

2

需要使用列表吗? 使用 IEnumerable 方案可能更加高效:

 public IEnumerable<IMyClass> ConvertItems(List<MyClass> input)
 {
    foreach (var item in input)
    {
        yield return (IMyClass)item;
    }
 } 

1
值得一提的是,您不一定需要这样做才能使用IEnumerable解决方案。该方法可以只执行return input;。这意味着底层对象仍然是列表,因此您可以返回到它 - 这是上述方法中找不到的问题。但是,协变意味着直接转换为IEnumerable<IMyClass>是非常可能的。不确定是否自从7年前首次提出这个答案以来就有了这个新变化。我不追踪c#的历史,但现在肯定是有效的。 :) - Chris

0

(.NET 3.5 解决方案)

List<MyClass> list = new List<MyClass> { ... };
List<IMyClass> converted = list.Cast<IMyClass>().ToList();

converted 是一个 IEnumerable<IMyClass> 而不是一个 List<IMyClass> - CodesInChaos
OP问道:“我该如何将...转换为List<IMyClass>”,但你不是将其转换为List<T>,而是IEnumerable<T> - CodesInChaos
@CodeInChaos:你说得对,那是我的第一个版本,最近我改用了IEnumerable<T>。已经回滚了。谢谢。 - abatishchev

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