如何从基类列表中获取子类对象?

时间:2018-10-31 09:10:34

标签: c# .net inheritance

让我们考虑一个示例层次结构

class BaseClass{}

class ChildClass1 : BaseClass{}

class ChildClass2 : BaseClass{}

现在我有一个类型为BaseClass的通用列表

List<BaseClass> sampleList = new List<BaseClass>();

在此列表中,我同时添加了ChildClass1ChildClass2的对象。

现在访问这些对象时,我想对不同类型的对象执行不同的操作。 我可以使用if-else并检查对象是否为所需类型来轻松实现这一目标。

 class SampleClass
 {
     int Property{get;set;}
     List<BaseClass> sampleList = new List<BaseClass>();
     void DoCalculations()
     {
        foreach(var obj in sampleList)
        {
            if(obj is ChildClass1){//Do something to this.Property}
            else if(obj is ChildClass2){//Do Somethingto this.Property}
         }
     }
 }

但是由于复杂性和性能问题,我想避免使用if-elseswitch(我的实际情况包含大量派生类)。

因此,为了解决这种情况,我想出了一种方法,可以使用名称相同但类型不同的重载方法(每个派生类型均适用)。

我想到了以某种方式迭代并将BaseClass强制转换为ChlidClass对象,并将其传递给DoSomething()方法,而重载本身将照顾要执行的操作。但是,我无法从BaseClass获取ChildClass类型的对象来动态工作。

我尝试过的是:

class SampleClass
{
    int Property{get;set;}
    List<BaseClass> sampleList = new List<BaseClass>();

    public void DoCalculations()
    {
        foreach(var entry in sampleList)
        {
            Type type = entry.GetType();
            var obj = entry as type; //getting error here
            this.DoSomething(obj);
        }
    }

    private void DoSomething(ChildClass1 obj){//Do something to this.Property}

    private void DoSomething(ChildClass2 obj){//Do something to this.Property}
}

在上面的代码中,我仅显示了一个属性int Property{get;set;}的使用,但在我的实际情况下,请考虑使用多个属性。 有什么方法可以满足我的需求?

编辑: 如以下注释中所提到的,以及在BaseClass()中具有DoSomething()方法并在ChildClasses中覆盖它们的一些答案,在我的情况下,这可能实际上不起作用,因为DoSomething()与在其中定义的类的全局字段属性一起使用不是特定于ChildClass的对象。

2 个答案:

答案 0 :(得分:5)

请考虑使您的类BaseClass abstract并为其提供一个abstract方法。 类型ChildClass1ChildClass2都将被强制覆盖DoSomething()

如果由于某种原因而无法使BaseClass抽象,则还可以使方法virtual然后在overrideChildClass1ChildClass2 < / p>

抽象方法:

abstract class BaseClass
{    
    public abstract void DoSomething();
}

class ChildClass1 : BaseClass{
    public override void DoSomething()
    {
        Console.WriteLine("Impl. 1");
    }
}

class ChildClass2 : BaseClass{

    private string _someRandomAttribute = "Impl. 2";
    public override void DoSomething()
    {
        Console.WriteLine(_someRandomAttribute);
    }
}

虚拟方法:

class BaseClass
{    
    public virtual void DoSomething()
    {
       Console.WriteLine("Base implementation for types that don't override DoSomething()");
    }
}

class ChildClass1 : BaseClass{
    public override void DoSomething()
    {
        Console.WriteLine("Impl. for type ChildClass1");
    }
}

class ChildClass2 : BaseClass{
    public override void DoSomething()
    {
        Console.WriteLine("Impl. for type ChildClass2");
    }
}

完成此操作后,您将可以执行以下操作:

foreach(var obj in sampleList)
{
    obj.DoSomething();
}

答案 1 :(得分:3)

您应在DoSomething中使BaseClass成为虚拟方法,并让派生类适当覆盖它。然后,您可以在任何实例上简单地调用DoSomething

class BaseClass
{
    virtual void DoSomething() { ... }
}
class Child1 : BaseClass
{
    override void DoSomething() { Console.WriteLine("Child1"); }
}

现在,您无需检查类型,因为实例知道其类型,因此运行时知道要调用的覆盖:

foreach(BaseClass obj in sampleList)
{
    obj.DoSomething();
}

如果您想强制派生类来实现它,甚至可以使用DoSomething方法abstract。恕我直言,除非您希望能够实例化BaseClass,否则这很有意义。