web-dev-qa-db-ja.com

C#オブジェクト構築後にコードを実行する方法(構築後)

以下のコードからわかるように、DoStuff()メソッドは、Childオブジェクトの構築中にInit()メソッドの前に呼び出されています。

私は多数の子クラスを持っている状況にいます。したがって、各子のコンストラクターでInit()の直後にDoStuff()メソッドの呼び出しを繰り返すことは、エレガントな解決策にはなりません。

子のコンストラクタの後に実行される親クラスにある種のポストコンストラクタを作成する方法はありますか?このようにして、そこで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
    }
}
41
asmo

これはどう:

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

オブジェクトを構築するための複雑なロジックがある場合は、FactoryMethodパターンを検討してください。

あなたの場合、私はそれを単純なものとして実装します

_public static Parent Construct(someParam)
_

いくつかのパラメーターを取り、それに基づいてインスタンス化する子クラスを決定するメソッド。コンストラクターからDoStuff()メソッド呼び出しを削除し、新しいインスタンスのConstruct()内で呼び出すことができます。

また、コンストラクターでvirtual/abstractメソッドの呼び出しを避ける必要があります。詳細については、この質問を参照してください: コンストラクターの仮想メンバー呼び出し

18
Jakub Konecki

いくつかのC#機能を使用した一般的なソリューションを紹介しましょう。このソリューションでは、オブジェクトの作成後にファクトリパターンを使用したり何かを呼び出したりする必要はなく、単一のメソッドでインターフェースを実装するだけで任意のクラスで機能することに注意してください。最初に、クラスが実装する必要のあるインターフェースを宣言します。

public interface IInitialize {
    void OnInitialize();
}

次に、このインターフェイスの静的拡張クラスを追加し、Initializeメソッドを追加します。

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

ここで、オブジェクトが完全に構築された直後にクラスとそのすべての子孫がイニシャライザを呼び出す必要がある場合は、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");
    }
}

トリックは、派生クラスが拡張メソッドInitializeを呼び出すと、実際のクラスから行われていない呼び出しを抑制します。

9
Márton Balassa

他の人が述べたように、ファクトリパターンを使用する必要があります。

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

パターンのようにFactoryを使用することを強くお勧めします。

可能であれば:

1。すべての子と抽象クラスをseparate Assemblyにプッシュします。

2。内部メソッドのように子のctorを宣言するので、そのAssemblyからだれもctorを呼び出すだけで子を構築できません。

。 Factoryクラスを実装して、呼び出し元が指定したオブジェクトタイプを構築します。これは、オブジェクトの実際の作成後、呼び出し元に返す前に、抽象DoStuff()メソッドの呼び出しを明白に強制します。

良いことこれについて:抽象化のレベルも上がるので、将来さらに関数呼び出しやその他のタイプの論理的な複雑さが必要になる場合は、それらをFactoryクラスに追加するだけです。

あれは。

よろしく

4
Tigran

WPFアプリケーションでは、Dispatcherを使用してDoStuff()の呼び出しを延期できます。

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

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

ただし、DoStuff()がコンストラクタの直後に呼び出されることは保証されていません。

2
hillin

修正:この答え に従って、いつを決定することはできません基本クラスのコンストラクターは、サブクラスの構築中に呼び出されます。

例えば。これは機能しません:

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

したがって、そうです、他の人が述べたように、イベントのシーケンスが重要な場合、基本クラスは、抽象メソッドを順番に宣言することによって、または(さらに良いことに)DoStuffの子クラスの実装に、イベント:

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

DoStuffは抽象的です。 DoStuffの上からInitを呼び出すだけです。

1
hoodaticus

すべての子孫クラスにメソッドを実装する必要があるabstractメソッドを使用する代わりに、次のことを試してください。

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 
}
0
3Dave