C#如何在对象构造后执行代码(postconstruction)

asm*_*smo 38 c# constructor code-design

正如您在下面的代码中看到的那样,在构造Child对象期间,在Init()之前调用DoStuff()方法.

我的情况是,我有很多子课.因此,在每个子节点的构造函数中的Init()之后直接重复调用DoStuff()方法将不是一个优雅的解决方案.

有没有办法在父类中创建某种post构造函数,它将在子构造函数之后执行?这样,我可以在那里调用DoStuff()方法.

如果您有任何其他设计理念可以解决我的问题,我也想听听它!

abstract class Parent
{
    public Parent()
    {
        DoStuff();
    }

    protected abstract void DoStuff();
}

class Child : Parent
{
    public Child()
    // DoStuff is called here before Init
    // because of the preconstruction
    {
        Init();
    }

    private void Init()
    {
        // needs to be called before doing stuff
    }

    protected override void DoStuff() 
    {
        // stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

Jak*_*cki 18

如果您有构造对象的复杂逻辑,那么请考虑FactoryMethod模式.

在你的情况下,我会把它作为一个简单的实现

public static Parent Construct(someParam)
Run Code Online (Sandbox Code Playgroud)

采用一些参数并基于它的方法决定实例化哪个子类.您可以DoStuff()从构造函数中删除方法调用,并Construct()在新实例上调用它.

此外,您应该避免构造函数中的虚拟/抽象方法调用.有关更多详细信息,请参阅此问题:构造函数中的虚拟成员调用


tay*_*onr 12

这个怎么样:

abstract class Parent
{
    public Parent()
    {
        Init();
        DoStuff();
    }

    protected abstract void DoStuff();
    protected abstract void Init();
}

class Child : Parent
{
    public Child()
    {
    }

    protected override void Init()
    {
        // needs to be called before doing stuff
    }

    protected override void DoStuff() 
    {
        // stuff
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果Child的Init()方法实现依赖于传递给Child的构造函数的参数怎么办? (6认同)
  • 在构造函数中调用抽象/虚拟方法可能会导致意外的行为/错误,下面的** Factory **方法是更好的IMO [在构造函数中的虚拟成员调用](http://stackoverflow.com/questions/119506/virtual-member-调用构造函数) (4认同)
  • -1在当前的实现中,您是否看到类层次结构和构造函数初始化本身都包含DoStuff()和Init()?在这种情况下,它是无用的垃圾代码 (2认同)
  • 这个答案不能解决问题,甚至会带来更多问题。子级的“ Init”方法假定子级是完全构造的,但事实并非如此。调用`Init`和`DoStuff`时,`Child`中的所有内容均未初始化。在这里查看我的答案以获取有效的解决方案:/sf/answers/2706173921/ (2认同)

Már*_*ssa 6

让我介绍一些使用C#功能的一般解决方案。请注意,此解决方案不需要您在构造对象后使用工厂模式或调用任何方法,它可以在任何类上工作,只需使用单个方法实现接口即可。首先,我们声明一个我们的类必须实现的接口:

public interface IInitialize {
    void OnInitialize();
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们为此接口添加一个静态扩展类,并添加Initialize方法:

public static class InitializeExtensions
{
    public static void Initialize<T>(this T obj) where T: IInitialize
    {
        if (obj.GetType() == typeof(T))    
            obj.OnInitialize();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们需要一个类及其所有后代在对象完全构造后立即调用初始化程序,那么我们要做的就是实现IInitialize并在构造函数中添加一行:

public class Parent : IInitialize
{
    public virtual void OnInitialize()
    {
        Console.WriteLine("Parent");
    }

    public Parent()
    {
        this.Initialize();
    }
}

public class Child : Parent
{
    public Child()
    {
        this.Initialize();
    }

    public override void OnInitialize()
    {
        Console.WriteLine("Child");
    }
}

public class GrandChild : Child
{
    public GrandChild()
    {
        this.Initialize();
    }

    public override void OnInitialize()
    {
        Console.WriteLine("GrandChild");
    }
}
Run Code Online (Sandbox Code Playgroud)

诀窍是,当派生类调用扩展方法Initialize时,它将抑制任何未从实际类进行的调用。

  • 这样您就能更接近您需要的东西。CLR 无法在构造函数之后自动运行方法。但是,您仍然可以创建一个静态工厂来为您调用 Initialize(并消除构造函数中的 Initialize 调用): (2认同)

Nic*_*ter 5

正如其他人所提到的,您应该使用工厂模式。

public class Parent
{
    public Parent()
    {            
    }

    public virtual void PostConstructor()
    {
    }
}

public class Child : Parent
{
    public override void PostConstructor()
    {
        base.PostConstructor();

        // Your code here
    }
}

public void FactoryMethod<T>() where T : Parent
{
    T newobject = new T();
    newobject.PostConstructor();
}
Run Code Online (Sandbox Code Playgroud)