6
votes

C # Constructeur enchaînant - Modification de l'ordre d'exécution

Je veux savoir comment modifier l'ordre d'exécution lors du chaînage des constructeurs en C #. Les seules méthodes que j'ai constatées nécessitent que le constructeur enchaîné soit appelé premier, à l'extérieur du constructeur actuel.

Spécifiquement, prenez l'exemple suivant: p> xxx pré> Idéalement, je ' J'aime réduire la répétition du code en faisant ceci (note, j'avoue que dans cet exemple de contreveillé, pas beaucoup de code n'est enregistré, mais je travaille avec le code qui profiterait beaucoup plus. J'utilise cet exemple de clarté): P>

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    if (ThingCache.ContainsKey(name)) {
      this(ThingCache[name]);
    } else {
      this(ExternalStaticFactory.GetThing(name));
      ThingCache.Add(name, myThing);
    }
  }

  public Foo(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}


0 commentaires

4 Réponses :


1
votes

Vous créez des méthodes privées initialisation em> à l'intérieur de votre classe et demandez à votre logique de constructeur ces méthodes.

class Foo
{
 public Foo(string name) 
 {
   InitializeBefore();

   if (ThingCache.ContainsKey(name)) 
   {
      myThing = ThingCache[name];
   } else 
   {
     myThing = ExternalStaticFactory.GetThing(name);
     ThingCache.Add(name, myThing);
   }

   InitializeAfter();
 }

 public Foo(Thing tmpThing) 
 {
   InitializeBefore();
   myThing = tmpThing;
   InitializeAfter();
 }

 private void InitializeBefore() 
 {
   doSomeStuff();
   // and any other calls you want before 
 }

 private void InitializeAfter() 
 {
   doSomeOtherStuff();
   // and any other calls you want at the end of the constructor
 }

}


3 commentaires

Bien sûr, cela ne joue pas bien avec les champs initonly (C # Lisonly mot-clé).


Je ne vois pas comment créer des méthodes d'initialisation vous permet d'appeler un constructeur à partir d'un autre constructeur. En fait, il semble que cela ne ferait que décharger le problème - maintenant, vous avez la répétition dans les méthodes d'initialisation plutôt que dans les constructeurs. Je ne vois pas cela comme une amélioration.


Votre exemple ressemble exactement à mon exemple original - la chose que j'essayais d'éviter de faire en utilisant un chalamon du constructeur. Le problème est que maintenant la logique de l'appel initialize avant () et d'initialisation () doit être dans chaque constructeur. C'est exactement ce que je veux éviter. Un seul constructeur devrait savoir que ces méthodes doivent être appelées et que tous les autres constructeurs devraient pouvoir bénéficier de cela.



7
votes

Je veux savoir comment modifier l'ordre d'exécution lors de la chaîne des constructeurs en C #.

vous ne le faites pas. Il n'y a pas de telle caractéristique dans c #.

Il existe déjà un mécanisme permettant d'appeler du code arbitraire dans un ordre arbitraire: faire un tas de méthodes et les appeler dans l'ordre que vous aimez.

Voici mon article sur le sujet en passant dans plus de détails.

http: //blogs.msdn.com/b/ericlippert/archive/2010/01/28/calling-constructors-in-arbitrary-places.aspx

Si le sujet du constructeur enchaînant des principes de conception vous intéresse, vous voudrez peut-être aussi lire

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/whay-do-initializers-run-in-the-opposite- AS-constructeurs-part-one.aspx

et

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/whay-do-initializers-run-in-the-opposite- AS-constructeurs-part-deux.aspx


2 commentaires

Le point est que si je mets le code dans différentes méthodes, chaque constructeur doit savoir qu'ils doivent tous être appelés et dans le bon ordre. Si je peux chaîner des constructeurs dans un ordre arbitraire (comme dans VB .NET), un seul constructeur doit le savoir. De plus, les méthodes extérieures ne fonctionneront pas avec les membres Initially, comme mentionné Ben ci-dessous. Ce que vous obtenez, c'est toujours la répétition de code, vous en réduisez un peu un peu en répétant les appels de méthode répétés plutôt que dans l'ensemble du morceau du code dans la méthode.


Ne vous permettant pas de décider quand un constructeur chaîné est appelé en C # était une décision de conception plutôt malheureuse. J'ai rencontré plusieurs fois des scénarios complexes pouvant être abordés très facilement si cela était juste autorisé: /



8
votes

Vous ne pouvez pas appeler constructeurs dans d'autres constructeurs. Un constructeur ne peut que canaliser un autre constructeur à appeler directement avant de cela.

Cependant, pour résoudre votre problème particulier, vous pouvez créer un constructeur privé capable d'accepter à la fois un type d'argument et que vos deux constructeurs originaux ont tous deux la chaîne. Un, passant null pour l'argument manquant.

Ceci a l'avantage de faire appel à des méthodes d'initialisation privées qu'il joue bien avec les champs lisonly (c.-à-d. Fonctionne toujours si MOWTHINT < / code> est un champ lisonly ): xxx

Vous pouvez également utiliser des arguments nommés ou facultatifs si vous utilisez C # 4.0:

http://msdn.microsoft.com/en-us/library/ dd264739.aspx


2 commentaires

J'aime cette réponse. C'est un peu désordonné - pas aussi simple et agréable que la capacité d'appeler le constructeur d'un point arbitraire serait, mais cela accomplit définitivement ce que je cherchais sans aucun des inconvénients des méthodes d'initialisation des appels (). Bonne pensée, merci! Une chose que j'ajouterais probablement, c'est que le nouveau constructeur composite (celui qui prend des arguments) devrait probablement être privé - car ce n'est pas vraiment un point d'entrée «normal» pour un appelant à utiliser. Je ne pense pas que je voudrais que quiconque essaie jamais d'appeler celui-là directement.


Ouais. Fait le constructeur de deux arguments privé maintenant.



0
votes

Vous devez utiliser une méthode. La traduction la plus simple serait la suivante:

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    if (ThingCache.ContainsKey(name)) {
      Init(ThingCache[name]);
    } else {
      Init(ExternalStaticFactory.GetThing(name));
      ThingCache.Add(name, myThing);
    }
  }

  public Foo(Thing tmpThing) {
    Init(tmpThing);
  }

  private void Init(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}


0 commentaires