-->

C# How to execute code after object construction (

2019-02-16 03:51发布

问题:

As you can see in the code below, the DoStuff() method is getting called before the Init() one during the construction of a Child object.

I'm in a situation where I have numerous child classes. Therefore, repeating a call to the DoStuff() method directly after Init() in the constructor of each child wouldn't be an elegant solution.

Is there any way I could create some kind of post constructor in the parent class that would be executed after the child's constructor? This way, I could call to the DoStuff() method there.

If you have any other design idea which could solve my problem, I'd like to hear it too!

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
    }
}

回答1:

How about this:

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
    }
}


回答2:

If you have a complex logic for constructing your objects then consider FactoryMethod pattern.

In your case I would implement it as a simple

public static Parent Construct(someParam)

method that takes some parameter and based on it decides which child class to instantiate. You can remove your DoStuff() method call from the constructor and call it inside Construct() on the new instance.

Also, you should avoid virtual/abstract method calls in the constructors. See this question for more details: Virtual member call in a constructor



回答3:

I would strongly suggest use Factory like a pattern.

If it's possible:

1. Push all your childs and abstract class into separate assembly.

2. Declare ctors of childs like internal methods, so no one out of that assembly is can construct them just by calling ctor.

3. Implement the Factory class to construct for caller specified objects type, which obviuoly will forse calling of abstract DoStuff() method after actual creation of anobject, but before returning it to caller.

Good thing about this is that: It will give you also additional level of abstraction, so if in the future you will need some more functions call or any other type of logical complexity, what you will need, is just add them into your Factory class.

That is.

Regards



回答4:

As others have mentioned, you should use a Factory Pattern.

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


回答5:

Let me introduce a general solution using some C# features. Note that this solution does not require you to use a factory pattern or invoke anything after constructing the object, and it works on any class with just implementing an interface with a single method. First we declare an interface that our classes will have to implement:

public interface IInitialize {
    void OnInitialize();
}

Next we add a static extension class for this interface, and add the Initialize method:

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

Now, if we need a class and all of its descendants to call an initializer right after the object is fully constructed, all we need to do is implement IInitialize and append a line in the constructor:

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

The trick is that when a derived class calls the extension method Initialize, that will suppress any calls not made from the actual class.



回答6:

Correction: As per this answer, you can't determine when the base class's constructor is invoked during construction of the subclass.

E.g. This doesn't work:

public Child()
// DoStuff is called here after Init
// because of the overridden default constructor
{
    Init();
    base();
} 

So, yes, as others have noted, if sequence of events matters, then the base class needs to be able to accommodate that by declaring abstract methods in order, or (better yet) by having the child class's implementation of DoStuff represent the sequence of events:

protected override void DoStuff()
{
    Init();
    base.DoStuff();
}


回答7:

DoStuff is abstract. Just call Init from the top of DoStuff.



回答8:

In WPF applications, you can postpone the invokation of DoStuff() with the help of Dispatcher:

abstract class Parent
{
    public Parent()
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(this.DoStuff));
    }

    private void DoStuff()
    {
        // stuff, could also be abstract or virtual
    }
}

However, it is not guaranteed that DoStuff() will be called immediately after the constructor.



回答9:

Rather than using an abstract method, which would require you to implement the method in all descendant classes, you might try:

public class Parent
{
    public Parent()
    {
        PostConstructor();
    }


    protected virtual void PostConstructor()
    {
    }
}

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

        /// do whatever initialization here that you require
    }
}

public class ChildWithoutOverride
{
    /// not necessary to override PostConstructor 
}