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

时间:2017-06-20 18:19:31

标签: c# oop inheritance parent-child

我的同事告诉我,有可能将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();

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

4 个答案:

答案 0 :(得分:10)

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

假设您有类型AnimalCat(继承Animal)和Dog(继承Animal)。 List<Animal>可以同时包含Cats和Dogs,但List<Dog>不能包含Cats。如果您的List<Animal>包含Cat和Dog对象,那么您所做的任何事情都不会让您生成包含原始列表中所有内容的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对象的属性将更改原始对象,因为它是相同的对象实例。

答案 1 :(得分:1)

Cast<T>方法将强制转换操作应用于输入序列的所有元素。仅当您可以对序列的每个元素执行以下操作而不会导致异常时,它才有效:

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

除非为p分配了ChildClass或其子类之一,否则这将无效。在您的情况下,从服务器API返回的数据似乎包含ParentClassChildClass以外的其中一个子类的对象。

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

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

答案 2 :(得分:1)

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

List<ParentClass> parentList = ServiceApi.GetParentClassList().ToList();
List<ChildClass> childList = parentList
    .Where(x => x is ChildClass)
    .Select(x => x as ChildClass)
    .ToList();

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

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

请参阅此.NET小提琴:https://dotnetfiddle.net/gDsNKi

答案 3 :(得分:1)

如果您愿意丢失演员表失败的项目,您可以使用OfType()方法仅返回演员成功的项目。

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