将基类列表转换为派生类列表 - 可能吗?

问题描述 投票:0回答:5

我的同事告诉我,可以将

List<BaseClass>
转换为
List<DerivedClass>
(他们没有向我展示工作示例)。我认为不可能将父类转换为子类(但我确实知道可以反过来做)。我看到了几个相关的问题,有人说它不能完成,有人说它可以 - 所提出的解决方案对我来说根本不起作用。我总是得到:

System.InvalidCastException.
Unable to cast object of type 'BaseClass' to 'ChildClass'

这绝对可能吗?如果是这样,我做错了什么?这是我的代码(为了这个问题的目的而简化):

家长班:

public class ParentClass
{
    public string Name = "";
}

少儿班:

public class ChildClass: ParentClass
{
    public string Date = "";
}

转换:

List<ParentClass> parentList = ServiceApi.GetParentClassList().ToList();
List<ChildClass> childList = parentList.Cast<ChildClass>().ToList();

我的实际代码要复杂得多,但应该应用相同的原则。

c# oop inheritance parent-child
5个回答
28
投票

我的同事告诉我,可以将

List<BaseClass>
转换为
List<DerivedClass>
(他们没有向我展示工作示例)。我没想到可以将父类转换为子类

从某种意义上来说,你们都是对的。

假设您有类型

Animal
Cat
(继承 Animal)和
Dog
(继承 Animal)。
List<Animal>
可以同时包含猫和狗,但
List<Dog>
不能包含猫。如果您有一个同时包含 Cat 和 Dog 对象的
List<Animal>
,那么无论您做什么都不会让您生成包含原始列表中所有内容的
List<Dog>

所以从这个意义上说,你是对的,你不能从

List<DerivedClass>
产生
List<ParentClass>

但有时,作为程序员,您拥有编译器无法获得的有关如何使用对象的信息。如果(且仅当)您可以知道特定的

List<Animal>
实例仅包含
Dog
对象,您始终可以编写如下内容:

List<Dog> dogList = myAnimalListWithOnlyDogs.Cast<Dog>().ToList();

再次强调:只有当您可以保证列表实例中的每个对象都可以转换为 Dog 对象时,这才有效。如果 Cat 对象以某种方式进入您的列表,您将在运行时遇到异常。尽管如此,这样的代码并不罕见。

从这个意义上来说,这位同事是对的。当涉及到真正的工作代码时,人们无论如何都会取得很好的效果,并且很好地摆脱它,尽管纯粹主义者可能会认为这是一种代码味道,表明需要使用组合而不是继承。

此外,有时

List<Animal>
实例可能同时包含 Cat 和 Dog 对象,而您只关心
Dog
对象。在这种情况下,你可以这样做:

List<Dog> dogList = myAnimalList.OfType<Dog>().ToList();

我经常看到这种模式的使用,尤其是在使用 WinForms 控件集时。

在上面的两个示例中,您正在使用“新序列”。在新序列中添加或删除对象不会改变原始序列。从这个意义上说,您“没有”转换原始文件,而是创建了替代品。但是,该序列包含相同的对象,因此在新序列中编辑 Dog 对象的属性更改原始序列中的该对象,因为它是相同的对象实例。

Cast<T>

4
投票

ParentClass p = ... ChildClass c = (ChildClass)p;


除非为

p
分配了
ChildClass

或其子类之一的实例,否则这将不起作用。在您的情况下,从服务器 API 返回的数据似乎包含

ParentClass
或其子类之一而不是
ChildClass
的对象。

假设您从服务器获得了足够的信息,您可以通过构造
ChildClass
实例来解决此问题:

List<ChildClass> childList = parentList .Select(parent => new ChildClass(parent.Name, ... /* the remaining fields */)) .ToList();

    

如果您愿意在施放失败时丢失物品,可以使用 
OfType()

2
投票

var children = parentList.OfType<ChildClass>().ToList();

    

未经测试,但这应该有效:

1
投票

您还可以使用评论中提到的 OfType:

List<ChildClass> childList = parentList
    .OfType<ChildClass>()
    .ToList();

参见这个 .NET Fiddle:

https://dotnetfiddle.net/gDsNKi

    

他们没有向我展示有效的示例。

1
投票
如果没有帮助,从基类到派生类是不可能的。我使用反射来解决这个问题。

在派生类中包含一个使用反射从基类填充的函数。

public class DerivedClass : BaseClass { public DerivedClass PopulateBaseClass(object baseClass) { var parentProperties = baseClass.GetType().GetProperties(); var childProperties = this.GetType().GetProperties(); foreach (var parentProperty in parentProperties) { foreach (var childProperty in childProperties) { if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType) { childProperty.SetValue(this, parentProperty.GetValue(baseClass)); break; } } } return this; } public string SomeDerivedClassProperty { get; set; } }

将 BaseClass 列表转换为 DerivedClass 列表,如下所示

   List<BaseClass> items = new List<BaseClass>();
   // populate some base class items
   List<DerivedClass> derivedItems = items.Select(i => new DerivedClass().PopulateBaseClass(i)).ToList();

或者,如果您的派生类未使用

JsonProperty
 定义备用属性名称,您可以使用 Json 来处理繁重的工作

List<DerivedClass> derivedItems = JsonConvert.DeserializeObject<List<DerivedClass>>(JsonConvert.SerializeObject(items));

    

© www.soinside.com 2019 - 2024. All rights reserved.