【问题标题】:How to get a child class object from base class list?如何从基类列表中获取子类对象?
【发布时间】:2018-10-31 09:10:34
【问题描述】:

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

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;} 的使用,但在我的实际案例中,考虑了多个属性。 有什么方法可以实现我的需要吗?

编辑: 正如下面提到的 cmets 和在 BaseClass() 中有一个方法并在 ChildClasses 中覆盖它们的几个答案,这在我的情况下可能实际上不起作用,因为 DoSomething() 与它定义的类的全局字段属性一起工作. 不特定于 ChildClass 的对象。

【问题讨论】:

  • 使DoSomething 成为BaseClass 的实例方法并在派生类中覆盖它。
  • @Adrian 我已经更新了关于为什么您建议的方法可能不适用于我的要求的问题。
  • 您可以通过该类中的覆盖轻松访问Child1 中声明的任何字段。我没看到问题。也许您应该创建另一个问题或至少发布当前代码
  • LINQ 的OfType 在这里有帮助吗?
  • "定义它的类的全局字段属性。它不特定于 ChildClass 的对象。"是否定义了这些字段/属性(没有全局字段属性,它是静态/实例字段或静态/实例属性)?你能多展示一些你的课程吗?

标签: c# .net inheritance


【解决方案1】:

考虑让你的类BaseClassabstract 并给它一个abstract 方法。 ChildClass1ChildClass2 两种类型都将被强制覆盖 DoSomething()

如果由于某种原因您无法将 BaseClass 抽象化,您也可以在 ChildClass1ChildClass2 中创建 virtualoverride 方法

抽象方法:

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();
}

【讨论】:

  • 我已经更新了关于为什么您建议的方法可能不适用于我的要求的问题。
  • @JasmeetsinghBansal 它仍然可以使用类的属性/属性工作。请参阅我的更新答案。
  • 我指的是包含 sampleList 的类的属性和字段。
  • 嗯...当您拥有 BaseClass 列表时,您将只能访问 BaseClass 类型的属性和字段。如果您确实需要特定的属性,那么只有一个或另一个类具有您将不得不像您在问题中所做的那样转换它们。顺便说一句:一个(工作)努力的演员不会对性能产生太大影响,因为它不会做任何昂贵的事情
  • @Dominik 昂贵是非常主观的,取决于您的上下文以及您执行代码的频率。如果在循环中执行一百万次,即使是简单的演员表也可能会花费一些时间。但我同意这在这里可能不是一个大问题。
【解决方案2】:

您应该使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

【讨论】:

  • @HimBromBeere 我已经更新了关于为什么您建议的方法可能不适用于我的要求的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多