web-dev-qa-db-fra.com

C # Comment exécuter du code après la construction d'un objet (postconstruction)

Comme vous pouvez le constater dans le code ci-dessous, la méthode DoStuff () est appelée avant la méthode Init () lors de la construction d'un objet Child.

Je suis dans une situation où j'ai de nombreux cours pour enfants. Par conséquent, répéter un appel à la méthode DoStuff () directement après Init () dans le constructeur de chaque enfant ne serait pas une solution élégante.

Existe-t-il un moyen de créer un type de constructeur de publication dans la classe parent qui serait exécuté après le constructeur de l'enfant? De cette façon, je pourrais appeler la méthode DoStuff () ici.

Si vous avez une autre idée de design qui pourrait résoudre mon problème, j'aimerais aussi l'entendre!

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

Que dis-tu de ça:

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

Si vous avez une logique complexe pour construire vos objets, alors considérezFactoryMethodpattern.

Dans votre cas, je le mettrais en œuvre comme un simple 

public static Parent Construct(someParam)

la méthode qui prend un paramètre et en fonction de laquelle elle décide quelle classe enfant instancier. Vous pouvez supprimer votre appel de méthode DoStuff() du constructeur et l’appeler dans Construct() sur la nouvelle instance.

En outre, vous devez éviter les appels de méthodes virtuelles/abstraites dans les constructeurs. Voir cette question pour plus de détails: Appel de membre virtuel dans un constructeur

16
Jakub Konecki

Je suggère fortement d'utiliser Factory comme un motif. 

Si c'est possible: 

1 . Poussez tous vos enfants et vos classes abstraites dans séparez Assembly

2 . Déclarant les acteurs des enfants comme des méthodes internes, aucun membre de cette Assemblée ne peut les construire simplement en appelant ctor.

3 . Implémentez la classe Factory pour construire le type d'objet spécifié par l'appelant, ce qui évitera évidemment l'appel de la méthode abstraite DoStuff () après la création d'un objet, mais avant de le renvoyer à l'appelant. 

La bonne chose à propos de ceci est que: Il vous donnera également un niveau supplémentaire d'abstraction, donc si à l'avenir vous aurez besoin de plus d'appels de fonctions ou de tout autre type de complexité logique, vous aurez simplement besoin de les ajouter dans votre classe d'usine. 

C'est. 

Cordialement

4
Tigran

Permettez-moi de vous présenter une solution générale utilisant certaines fonctionnalités de C #. Notez que cette solution ne vous oblige pas à utiliser un modèle d'usine ni à invoquer quoi que ce soit après la construction de l'objet et qu'elle fonctionne sur toutes les classes en implémentant simplement une interface avec une seule méthode. Nous déclarons d'abord une interface que nos classes devra mettre en œuvre:

public interface IInitialize {
    void OnInitialize();
}

Ensuite, nous ajoutons une classe d'extension statique pour cette interface et ajoutons la méthode Initialize:

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

Maintenant, si nous avons besoin d'une classe et de tous ses descendants pour appeler un initialiseur juste après la construction complète de l'objet, il suffit d'implémenter IInitialize et d'ajouter une ligne dans le constructeur:

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

Le truc, c’est que quand une classe dérivée appelle la méthode d’extension Initialize, elle supprime tous les appels qui n’ont pas été passés à partir de la classe réelle.

4
Márton Balassa

Comme d'autres l'ont mentionné, vous devriez utiliser un modèle d'usine.

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

Dans les applications WPF, vous pouvez différer l'invocation de DoStuff() à l'aide de Dispatcher:

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

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

Cependant, il n'est pas garanti que DoStuff() sera appelé immédiatement après le constructeur.

1
hillin

Correction: Selon cette réponse , vous ne pouvez pas déterminer when le constructeur de la classe de base est appelé lors de la construction de la sous-classe.

Par exemple. Cela ne marche pas:

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

Ainsi, comme d'autres l'ont déjà souligné, si la séquence d'événements est importante, la classe de base doit pouvoir l'adapter en déclarant des méthodes abstraites dans l'ordre, ou (mieux encore) en faisant en sorte que l'implémentation de DoStuff par la classe enfant représente la séquence de événements:

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

DoStuff est abstrait. Appelez simplement Init depuis le sommet de DoStuff.

1
hoodaticus

Plutôt que d'utiliser une méthode abstract, qui nécessiterait de l'implémenter dans toutes les classes descendantes, vous pouvez essayer:

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