2008-12-02 5 views
49

J'ai deux classes, Foo et Bar, qui ont des constructeurs comme celui-ci:Appel surchargée Constructor et Constructor Base en C#

class Foo 
{ 
    Foo() 
    { 
     // do some stuff 
    } 

    Foo(int arg) 
    { 
     // do some other stuff 
    } 
} 

class Bar : Foo 
{ 
    Bar() : base() 
    { 
     // some third thing 
    } 
} 

Maintenant, je veux présenter un constructeur Bar qui prend un int, mais je veux ce qui se passe dans Bar() pour exécuter ainsi que comme le truc de Foo (int). Quelque chose comme ceci:

Bar(int arg) : Bar(), base(arg) 
{ 
    // some fourth thing 
} 

Y at-il un moyen de le faire en C#? Le meilleur que j'ai jusqu'ici est de mettre le travail fait par Bar() dans une fonction, qui est également appelée par Bar (int), mais c'est assez inélégant.

Répondre

23

Non, ce n'est pas possible. Si vous utilisez Reflector pour examiner l'IL générée pour chaque constructeur, vous verrez pourquoi: vous finirez par appeler les deux constructeurs de la classe de base. En théorie, le compilateur pourrait construire des méthodes cachées pour accomplir ce que vous voulez, mais il n'y a vraiment aucun avantage sur vous à faire la même chose explicitement.

+1

Probablement downvoted parce que vous n'avez pas donné de solution au problème, même s'il y en a clairement un. –

1

Vous ne pouvez pas avoir le constructeur de barre qui prend un int invoquer le constructeur sans paramètre?

+0

Il veut également appeler le constructeur paramétré de la classe de base. –

+0

D'accord, je vois maintenant. –

1

Pouvez-vous mettre les choses de Bar() dans Bar (int) et appelez Bar (int) avec Bar() avec une valeur par défaut? Alors Bar (int) peut appeler le constructeur de base.

class Bar : Foo 
{ 
    Bar() : this(0) 
    { 
    } 

    Bar(int arg) : base(arg) 
    { 
    } 
} 

qui ne répond pas exactement à votre question, mais en fonction de votre scénario pourrait être une solution viable.

32

Je constructeurs re-chaîne, de sorte qu'ils sont appelés comme

Bar() : this(0) 
Bar(int) : Foo(int) initializes Bar 
Foo(int) initializes Foo 
Foo() : this(0) 

Ceci est approprié, si les constructeurs assument sans paramètre une sorte de valeur par défaut pour le paramètre int d'autres constructeur. Si les constructeurs ne sont pas liés, vous faites probablement quelque chose de mal avec votre type, ou peut-être que nous avons besoin de plus d'informations sur ce que vous essayez d'atteindre.

3

C'est ce que je peux penser ...

public class Foo 
{ 
    public Foo() 
    { 
    } 
    public Foo(int? arg): this() 
    { 
    } 

} 
public class Bar : Foo 
{ 
    private int x; 
    public Bar(): this(new int?()) // edited to fix type ambiguity 
    { 
     // stuff that only runs for paramerless ctor 
    } 
    public Bar(int? arg) 
     : base(arg) 
    { 
     if (arg.HasValue) 
     { 
      // Do stuff for both parameterless and parameterized ctor 
     } 
     // Do other stuff for only parameterized ctor 
    } 
} 
+0

Vous avez +1 pour l'idée nullable, mais il a un certain flux - si vous ajoutez un autre ctor avec un argument (de type classe, pas struct), il échouera, car maintenant (null) ne saura pas lequel choisir. –

+0

oui, merci pour cela! corrigé ... –

12

Je recommanderais de changer votre chaîne de constructeur pour passer du moins spécifique à la plus spécifique. Toute création de l'objet Bar appelle maintenant les 4 constructeurs. Le chaînage de constructeur devrait fournir des valeurs par défaut à des constructeurs plus spécifiques, et non l'inverse. Vous devriez vraiment regarder ce que vous essayez d'accomplir et vous assurer que ce que vous faites est logique. Curt a raison de dire qu'il y a des raisons techniques que vous ne pouvez pas faire, mais il y a aussi des raisons logiques pour lesquelles vous ne devriez pas.

1

pouvez-vous prendre le code d'initialisation pour Bar() et en faire une méthode et l'appeler des deux constructeurs, et le nouveau constructeur appelez simplement base (arg)?

0

Vous pouvez utiliser ce code:

public Foo 
{ 
    public Foo() 
    { 
     this.InitializeObject(); 
    } 

    public Foo(int arg) : this() 
    { 
     // do something with Foo's arg 
    } 

    protected virtual void InitializeObject() 
    { 
     // initialize object Foo 
    } 
} 

public Bar : Foo 
{ 
    public Bar : base() { } 

    public Bar(int arg) : base(arg) 
    { 
     // do something with Bar's arg 
    } 

    protected override void InitializeObject() 
    { 
     // initialize object Bar 

     base.InitializeObject(); 
    } 
} 

Juste remplacer la méthode InitializeObject() comme dans le code ci-dessus, et mettre tout votre code que vous voulez mettre en paramètre moins il constructeur. Et enfin appeler base.InitializeObject() à la fin du code.

Espérons que cela soit utile.

+0

Il est à noter que si les constructeurs peuvent écrire des champs 'readonly', une méthode' InitializeObject' ne sera pas capable de le faire directement. Pour initialiser de tels champs dans une méthode 'InitializeObject', il faut que le constructeur les passe comme paramètres' ref' ou 'out' (je préférerais' ref', puisque les valeurs des champs sont vides lors de la construction d'un objet, et puisque je n'aime pas les paramètres 'out' sur les méthodes virtuelles puisque les remplacements dans d'autres langages peuvent les considérer comme des paramètres' ref'). – supercat

+0

Vous pouvez le faire pour les champs en lecture seule: 'private readonly string _ro1 =" lecture seule 1 "; chaîne de lecture privée privée _ro2; private readonly int _ro3; public Bar(): base() {} barre publique (int arg): base (arg) {_ro3 = arg; _ro2 = "lecture seule:" + arg; } ' fondamentalement, si la valeur des champs readonly est déterminée par l'argument constructeur, vous pouvez mettre l'initialisation du champ sur le constuctor. Et sinon, vous pouvez mettre l'initialisation comme le premier champ en lecture seule ci-dessus (_ro1). – Yusup

+0

Votre réponse suggérait de déplacer le code d'initialisation du constructeur vers une méthode virtuelle. On peut initialiser des champs en lecture seule dans un constructeur ou un initialiseur de champ, mais on peut aussi le faire avec une méthode virtuelle, * si * on passe les champs en question comme paramètres 'ref' ou' out' à la méthode en question. BTW, une chose que j'aimerais vraiment voir dans vb.net et C# serait un moyen de définir un champ ou un pseudo-champ qui serait initialisé avec la valeur d'un paramètre constructeur, et pourrait être utilisé dans les initialiseurs de champ ultérieurs. Quelque chose comme: ... – supercat

Questions connexes