首页 > 解决方案 > 如何在继承的基本构造函数期间调用派生方法?

问题描述

我正在尝试在基本构造函数中调用派生方法。我希望下面的代码会打印出“Hello Derived”(特别是由于ParseData作为this.

using System;

public class Program
{
    public static void Main()
    {
        Derived a = new Derived("Hello");
    }

    public class Base
    {
        public Base(string s)
        {
            this.ParseData(s);
        }
        protected void ParseData(string s)
        {
            Console.WriteLine(s+ " Base");
        }
    }

    public class Derived : Base
    {
        public Derived(string s) : base(s){}
        new private void ParseData(string s)
        {
            Console.WriteLine(s + " Derived");
        }
    }
}

我从上一个问题的答案中理解了为什么它不能像我希望的那样工作 - 但是任何人都可以提供有关如何最好地实现我想要的效果的建议,而无需ParseData在构建完成后调用我的调用代码每次构造这个对象时 - 如下所示 - 如果可能的话。(我可能有很多不同的派生类来做到这一点......)

谢谢!

using System;

public class Program
{
    public static void Main()
    {
        string s = "Hello";
        Derived a = new Derived(s);
        a.ParseData(s);
    }

    public class Base
    {
        public Base(string s)
        {}
        protected void ParseData(string s)
        {
            Console.WriteLine(s+ " Base");
        }
    }

    public class Derived : Base
    {
        public Derived(string s) : base(s){}
        new private void ParseData(string s)
        {
            Console.WriteLine(s + " Derived");
        }
    }
}

标签: c#

解决方案


正如您所意识到的,您不能安全地从基类的构造函数中调用虚方法。

C# 允许您这样做,但是对派生类的重写方法的调用将在派生类的字段尚未初始化的上下文中执行——这可能会造成灾难。

您可以采取一种方法来缓解此问题。它并不完美,因为它让派生类有责任正确地做事,但至少意味着创建对象的客户端代码不需要做任何特别的事情。

它是这样的:

  1. 在基类声明中创建基类和要调用的抽象方法。这是为了强制派生类实现它。(如果你想从派生类调用一些公共代码,你可以在基类中给方法一个主体。)
  2. 制作基类的构造函数protected。如果基类是 ,这不是绝对必要的abstract,但这样做是常规的。
  3. 每个派生类都必须实现基类的抽象方法。
  4. 创建每个派生类的构造函数,private以便您可以强制客户端代码使用工厂方法来创建它(请参阅后面的步骤)。
  5. 每个派生类都必须实现一个静态工厂方法,该方法可用于创建该类的实例。这个方法应该调用被覆盖的方法。

听起来很多,但实际上可以归结为以下示例:

public abstract class Base
{
    protected Base(string s)
    {
        // Do something with s? Remove if not needed!
    }

    // Make this abstract so it must be overridden by a derived class.

    protected abstract void ParseData(string s);
}

public class Derived : Base
{
    public static Derived Create(string s)
    {
        var result = new Derived(s);
        result.ParseData(s);
        return result;
    }

    private Derived(string s) : base(s)  // Private to force use of Create().
    {
        // Whatever.
    }

    protected override void ParseData(string s)
    {
        Console.WriteLine(s + " Derived");
    }
}

现在,当客户端代码想要创建派生类的实例时,它必须使用Derived.Create(s)而不是new Derived(s)(由于构造函数是私有的,因此无法编译)。

当客户端代码确实创建了派生类的实例时,ParseData()将始终调用派生类,例如:

static void Main()
{
    var a = Derived.Create("Hello");
}

注意:我已经ParseData()保护了,就像您在原始代码中所做的那样,但它不必受到保护;必要时可以公开。


推荐阅读