2008-12-02 14 views
49

Mam dwie klasy Foo i bar, które mają konstruktory jak poniżej:Wywołanie przesłonięta Konstruktor i podstawa Konstruktor w C#

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

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

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

Teraz chcę wprowadzić konstruktora Bar, które odbywają int, ale chcę rzeczy, które zdarzają się w Bar(), aby uruchomić oraz jako rzeczy z Foo (int). Coś takiego:

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

Czy jest jakiś sposób to zrobić w C#? Najlepsze, co do tej pory, to oddanie pracy przez Bar() w funkcję, która również jest wywoływana przez Bar (int), ale jest to dość nieeleganckie.

Odpowiedz

23

Nie, nie jest to możliwe. Jeśli użyjesz Reflectora do zbadania IL, która została wygenerowana dla każdego konstruktora, zobaczysz dlaczego - w końcu wywołasz oba konstruktory dla klasy bazowej. Teoretycznie kompilator może konstruować ukryte metody, aby osiągnąć to, czego chcesz, ale naprawdę nie ma żadnej przewagi nad tym, że robisz to samo jawnie.

+1

Prawdopodobnie nie powiodło się, ponieważ nie rozwiązałeś problemu, mimo że wyraźnie istnieje. –

1

Nie możesz mieć konstruktora prętów, który przyjmuje int konstruktora bez parametrów?

+0

Chce również wywołać sparametryzowany konstruktor klasy bazowej. –

+0

Dobra, teraz widzę. –

1

Czy można umieścić rzeczy z Bar() w Bar (int) i wywołanie Bar (int) z Bar() z wartością domyślną? Następnie Bar (int) może wywołać konstruktora podstawowego.

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

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

To nie odpowiada dokładnie na twoje pytanie, ale w zależności od scenariusza może to być wykonalne rozwiązanie.

32

konstruktorzy bym ponownie łańcuch, tak jak są one nazywane

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

ta jest odpowiednia, jeśli konstruktorzy bez parametrów zakładamy jakąś wartość domyślna dla parametru int innego konstruktora. Jeśli konstruktorzy nie są powiązani, prawdopodobnie robisz coś nie tak z twoim typem, a może potrzebujemy więcej informacji o tym, co próbujesz osiągnąć.

3

Jest to jedyne co mogę myśleć o ...

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

Masz +1 dla idei nullable, ale ma on pewien przepływ - jeśli dodasz innego ctor z jednym argumentem (typu klasy, nie struktury), to się nie powiedzie, ponieważ teraz ta (null) nie będzie wiedziała, który z nich wybierać. –

+0

tak, dzięki za to! naprawiono ... –

12

Polecam zmieniając łańcuch konstruktora, aby przejść od najmniej do najbardziej specyficzny specyficzny.

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

Każde utworzenie obiektu Bar będzie teraz wywoływać wszystkie 4 konstruktory. Powiązanie konstruktora powinno dostarczać wartości domyślne do bardziej konkretnych konstruktorów, a nie na odwrót. Powinieneś naprawdę spojrzeć na to, co próbujesz osiągnąć i upewnić się, że to, co robisz, ma sens. Curt ma rację, że istnieją techniczne powody, dla których nie możesz tego zrobić, ale są też logiczne powody, dla których nie powinieneś.

1

Czy można wziąć kod inicjalizacji dla Bar() i uczynić go metodą i wywołać ją z obu konstruktorów, a nowy konstruktor po prostu wywołać base (arg)?

0

Można użyć tego kodu:

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

Wystarczy zastąpić metodę InitializeObject() podobnie jak w powyższym kodzie i umieścić cały kod, który chcesz umieścić w konstruktorze parametr mniej tam. I na koniec zadzwoń pod numer base.InitializeObject() na końcu kodu.

Mam nadzieję, że jest to przydatne.

+0

Warto zauważyć, że podczas gdy konstruktorzy mogą pisać pola 'readonly', metoda' InitializeObject' nie będzie w stanie tego zrobić bezpośrednio. Aby zainicjować takie pola w metodzie 'InitializeObject', trzeba mieć przekazany przez konstruktor jako parametry' ref' lub 'out' (wolałbym' ref', ponieważ wartości pól są zdefiniowane jako puste, gdy obiekt jest konstruowany, i ponieważ nie lubię parametrów "out" na wirtualnych metodach, ponieważ przesłonięcia w innych językach mogą je traktować jako parametry "ref"). – supercat

+0

Możesz to zrobić dla pól tylko do odczytu: 'private readonly string _ro1 =" tylko do odczytu 1 "; prywatny ciąg do odczytu _ro2; private readonly int _ro3; public Bar(): base() {} pasek publiczny (int arg): base (arg) {_ro3 = arg; _ro2 = "tylko do odczytu:" + arg; } ' Zasadniczo, jeśli wartość pól tylko do odczytu jest określona przez argument konstruktora, można ustawić inicjalizację pola na constuctor. A jeśli nie, możesz uruchomić inicjalizację tak jak pierwsze pole tylko do odczytu powyżej (_ro1). – Yusup

+0

Twoja odpowiedź sugerowała przeniesienie kodu inicjalizacyjnego z konstruktora do metody wirtualnej. Można inicjować pola tylko do odczytu w konstruktorze lub inicjatorze pola, ale można to również zrobić za pomocą metody wirtualnej, * jeśli * przekazuje dane pola jako parametry "ref" lub "out" do danej metody. BTW, jedna rzecz, którą naprawdę chciałbym zobaczyć w vb.net i C# byłaby środkiem do zdefiniowania pola lub pseudo-pola, które byłyby zainicjalizowane wartością w parametrze konstruktora, i mogłyby być użyte w późniejszych inicjalizatorach pól. Coś jak: ... – supercat