2008-12-02 19 views
49

Ich habe zwei Klassen, Foo und Bar, die Konstrukteuren wie dieses:Aufruf Überschrieben Constructor und Basiskonstruktor in C#

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

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

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

Jetzt möchte ich einen Konstruktor für Bar einzuführen, die einen int nimmt, aber ich will das Zeug, das in Bar() passiert, um sowie als das Zeug von Foo (int) zu laufen. So etwas wie dieses:

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

Gibt es eine Möglichkeit, dies in C# zu tun? Das Beste, was ich bisher getan habe, ist, die Arbeit von Bar() in eine Funktion zu bringen, die auch von Bar (int) aufgerufen wird, aber das ist ziemlich unelegant.

Antwort

23

Nein, das ist nicht möglich. Wenn Sie Reflector verwenden, um die IL zu untersuchen, die für jeden Konstruktor generiert wird, werden Sie sehen, warum - Sie würden beide Konstruktoren für die Basisklasse aufrufen. Theoretisch könnte der Compiler versteckte Methoden konstruieren, um das zu erreichen, was Sie wollen, aber es gibt keinen Vorteil gegenüber Ihnen, wenn Sie das Gleiche explizit tun.

+1

Wahrscheinlich falsch, weil Sie keine Lösung für das Problem gefunden haben, obwohl es eindeutig eins gibt. –

1

Können Sie nicht den Bar-Konstruktor, der einen int aufruft, den parameterlosen Konstruktor aufrufen?

+0

Er möchte auch den parametrisierten Konstruktor der Basisklasse aufrufen. –

+0

Okay, ich sehe jetzt. –

1

Können Sie die Sachen von Bar() in Bar (int) und Call Bar (int) mit Bar() mit einem Standardwert? Dann kann Bar (int) den Basiskonstruktor aufrufen.

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

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

Das beantwortet nicht genau Ihre Frage, aber abhängig von Ihrem Szenario könnte eine praktikable Lösung sein.

32

Ich würde wieder Kettenbauer, so werden sie genannt wie

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

Dies ist geeignet, wenn parameterlos Konstrukteure sind eine Art von Standardwert für int Parameter anderen Konstruktor übernehmen. Wenn Konstruktoren nicht verwandt sind, tun Sie wahrscheinlich etwas falsch mit Ihrem Typ, oder vielleicht brauchen wir mehr Informationen darüber, was Sie erreichen wollen.

3

Dies ist nur, was ich denken kann ...

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

Sie haben +1 für die Nullable-Idee, aber es hat einen gewissen Fluss - wenn Sie einen weiteren ctor mit einem Argument hinzufügen (des Klassentyps, nicht struct), wird es fehlschlagen, da jetzt (null) nicht wissen wird wählen. –

+0

ja, danke dafür! Es wurde behoben ... –

12

würde ich empfehlen, Ihre Konstruktor Kette Wechsel von gelinde spezifischsten spezifischen zu gehen.

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

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

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

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

Jede Erstellung des Bar-Objekts ruft jetzt alle 4 Konstruktoren auf. Die Konstruktorverkettung sollte für spezifischere Konstruktoren Standardwerte bereitstellen und nicht umgekehrt. Sie sollten sich wirklich anschauen, was Sie erreichen wollen und sicherstellen, dass das, was Sie tun, Sinn ergibt. Curt hat Recht, dass es technische Gründe gibt, warum man das nicht tun kann, aber es gibt auch logische Gründe, warum man das nicht tun sollte.

1

können Sie den Initialisierungscode für Bar() nehmen und es zu einer Methode machen und sie von beiden Konstruktoren aufrufen, und den neuen Konstruktor einfach base (arg) nennen?

0

können Sie diesen Code verwenden:

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

einfach überschreiben die InitializeObject() Methode wie in dem obigen Code, und stellen Sie alle Ihre Code, den Sie dort in parameterlose Konstruktor setzen wollen. Und schließlich rufen Sie base.InitializeObject() am Ende des Codes.

Ich hoffe, das ist nützlich.

+0

Es ist erwähnenswert, dass Konstruktoren zwar "readonly" -Felder schreiben können, eine 'InitializeObject'-Methode dies jedoch nicht direkt kann. Um solche Felder innerhalb einer 'InitializeObject'-Methode zu initialisieren, muss der Konstruktor sie als 'ref'- oder' out'-Parameter übergeben (ich würde' ref' bevorzugen, da Feldwerte beim Erstellen eines Objekts leer bleiben). und da ich Parameter auf virtuellen Methoden nicht mag, da Überschreibungen in anderen Sprachen sie als ref-Parameter betrachten können. – supercat

+0

Dies können Sie für schreibgeschützte Felder tun: 'private readonly string _ro1 =" read only 1 "; private readonly Zeichenfolge _ro2; privat readonly int _ro3; public Bar(): base() {} öffentlicher Balken (int arg): Basis (arg) {_ro3 = arg; _ro2 = "Nur lesen:" + arg; } ' Grundsätzlich, wenn der Wert der schreibgeschützten Felder durch das Konstruktorargument bestimmt wird, können Sie die Initialisierung des Feldes auf den Konstruktor setzen. Und wenn nicht, können Sie die Initialisierung genau wie das erste schreibgeschützte Feld über (_ro1) setzen. – Yusup

+0

Ihre Antwort bestand darin, einen Initialisierungscode aus dem Konstruktor in eine virtuelle Methode zu verschieben. Man kann schreibgeschützte Felder in einem Konstruktor oder einem Feldinitialisierer initialisieren, aber man kann dies auch mit einer virtuellen Methode tun, * wenn * die fraglichen Felder als "ref" - oder "out" -Parameter an die fragliche Methode übergeben werden. Übrigens, eine Sache, die ich gerne in vb.net und C# sehen würde, wäre ein Mittel, ein Feld oder Pseudofeld zu definieren, das mit dem Wert in einem Konstruktorparameter initialisiert wird und in späteren Feldinitialisierern verwendet werden könnte. Etwas wie: ... – supercat