【问题标题】:How can I cast List<BaseClass> to List<ChildClass> in C#?如何在 C# 中将 List<BaseClass> 转换为 List<ChildClass>?
【发布时间】:2013-09-27 00:02:53
【问题描述】:

假设我有以下课程:

public class BaseClass {
    ...
}

public class ChildClass : BaseClass {
    ...
}

如果我想在方法调用中将 ChildClass 的单个实例作为类型 BaseClass 传递,我可以这样做。

public void SomeCall(BaseClass baseClass)

....

ChildClass childClass = new ChildClass();
SomeCall(childClass);  <-- Works fine

但是,如果我想传入一个列表,我似乎无法正确地转换它。

public void SomeCall(List<BaseClass> baseClasses) 

....

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses);  <--  Gets compiler error

我得到的错误是

Argument 1: cannot convert from 'System.Collections.Generic.List<ChildClass>'
  to 'System.Collections.Generic.List<BaseClass>'

有没有办法将列表中的值转换为基类的类型?

现在,我想我只需要使用 foreach 并将列表中的每个对象自动映射到 BaseClass 的新列表。

【问题讨论】:

标签: c# list .net


【解决方案1】:

您最好的选择是在这里使用泛型,例如:

public void SomeCall<T>(List<T> list) where T: BaseClass
{
   foreach(T item in list)
   {
       // Assumes BaseClass defines method doSomething
       item.doSomething(....);
   }
}

调用方法做

SomeCall<ChildClass>(listOfChildren);

其他选择:

您可以只声明您的列表,以便它实际上以List&lt;BaseClass&gt; 继续。例如,

// Assumes Children extends BaseClass
List<BaseClass> list = new List<Children>();

考虑到您的应用程序,这可能会也可能不会,但如果您从未使用过泛型或无法使用泛型(不确定第一部分中的语法是否有效),这更容易低于 .NET 4.0。)

【讨论】:

  • 泛型是 C# 版本 (Visual Studio) 的一部分,它与 .NET 版本无关
  • 是的,但我不知道where T: ... 位在 4.0 以下是否有效,他们在 4.0 中添加了一些新的东西 co/contravariance。
  • 这个对我有用。我能够做这样的事情: SomeCall(childClasses.ToList());
【解决方案2】:

首先,你的方法是public void SomeCall(List&lt;BaseClass&gt; baseClasses)而不是public void SomeCall(IEnumerable&lt;BaseClass&gt; baseClasses)有什么原因吗?除非您特别需要将其作为 List&lt;BaseClass&gt; 来添加内容,否则您应该将其作为 IEnumerable&lt;BaseClass&gt; 传递,因为这样您可以灵活地执行以下任一操作:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.Cast<BaseClass>());

或:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.OfType<BaseClass>());

Cast 方法将尝试将List 的每个元素转换为BaseClass,如果任何一个转换失败,则将抛出,而OfType 方法将过滤Enumerable 的实例被强制转换为 BaseClass 并且只返回那些。

您不能像那样投射 List,因为正如 Eric Lippert 在 this answer 中回答的那样,如果 SomeCall 将其视为 List&lt;BaseClass&gt;,那么它可以向其中添加不是 ChildClass 的内容.你可以这样做:

List<ChildClass> childClasses = new List<ChildClass>();
SomeCall(childClasses.Cast<BaseClass>().ToList());

但这确实不是一个好主意,因为您将传递的实际上是一个新的List 实例。将SomeCall 的参数键入为List 而不是IEnumerable 的唯一原因是它需要修改列表,如果你传递它Cast&lt;BaseClass&gt;().ToList(),那么它所做的更改将立即丢​​失.

如果您不打算对List 进行更改,则应将其作为IEnumerable 传递并使用上述Linq 方法动态转换。如果您确实打算进行更改,则应将列表转换到其他位置并将其存储,以便您可以访问这些更改。

【讨论】:

    【解决方案3】:
    public interface IBaseClass
    {
    
    }
    public class BaseClass : IBaseClass
    {
    
    }
    
    public class ChildClass : BaseClass, IBaseClass {
    
    }
    
    public void SomeCall(IBaseClass BassClass)
    {
        BassClass.Dump();
    }
    
    public void SomeCall(List<IBaseClass> BassClasses)
    {
        BassClasses.Dump();
    }
    void Main()
    {
        List<IBaseClass> _ch = new List<IBaseClass>();
        _ch.Add(new ChildClass());
        _ch.Add(new ChildClass());
        _ch.Add(new ChildClass());
    
        SomeCall(_ch);
    }
    
    // Define other methods and classes here
    

    接口;这是一个 LinqPad 示例。

    【讨论】:

    • 我比我更喜欢@debracey 的回答……工作量少。大声笑
    • 泛型,它们是美丽的东西
    【解决方案4】:

    如果您的源列表可能包含该列表包含的超类型的不同子类型,您可以使用 Linq 执行以下操作:

    class SuperType
    {
      ...        
    }
    class SubType : SuperType
    {
      ... 
    }
    
    static void DoSomethingInteresting( List<SuperType list )
    {
      List<SubType> desired = list.Select( x => x as SubType )
                                  .Where( x => x != null )
                                  .ToList()
                                  ;
      ...
    }
    

    【讨论】:

      猜你喜欢
      • 2019-12-25
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 2013-06-02
      相关资源
      最近更新 更多