2010-09-26 9 views
63

W jakich warunkach mam wykonywać wywołania konstruktora :base() i :this() po nawiasach mojego konstruktora (lub nawet w innych miejscach kodu). Kiedy są te dobre praktyki i kiedy są one obowiązkowe?Najlepsze praktyki konstruktorów base() i this()

+0

przepraszam za niejasności ... wiem co te rozmowy robią. Po prostu nie miałem jasności co do scenariusza, kiedy powinienem lub muszę wykonywać te połączenia. Miejsca, w których jeśli ich nie wykonam, albo jestem od razu skazany na porażkę, albo utajony błąd, albo słaba jakość kodu. Ostatnio miałem taki ból głowy, dlatego chciałem wyjaśnić te scenariusze. - – explorer

Odpowiedz

85

: base(...)

Jeśli pominąć wywołanie konstruktora bazowym będzie wywołać konstruktora domyślnego bazowej automatycznie.

Wywołanie podstawowego konstruktora jest obowiązkowe, jeśli nie ma domyślnego konstruktora.

Nawet jeśli istnieje domyślny konstruktor, nadal możesz chcieć wywołać inny konstruktor niż domyślny konstruktor. W takim przypadku możesz nadal używać base(foo, bar) do wywoływania innego konstruktora niż konstruktor bazowy.

Nie uważam za złą praktykę pomijania base(), gdy chcesz wywołać konstruktor domyślny klasy podstawowej, chociaż jeśli chcesz być wyraźnie jawny, nie widzę żadnej szkody w jego włączaniu. To kwestia gustu.

: this(...)

Ta składnia pozwala na połączenie jednego konstruktora z innego podpisu od drugiego w tej samej klasie. Nigdy nie jest to obowiązkowe, ale czasami może się przydać.

Przykładem, kiedy może być użyteczne, jest ponowne użycie wspólnego kodu w konstruktorach. Na przykład w języku C# 3.5 lub wcześniej może chcesz symulować parametry opcjonalne w konstruktorze:

Foo(int x, int y) 
{ 
    this.x = x; 
    this.y = y; 
} 

Foo(int x) : this(x, 10) {} // y defaults to 10 

z C# 4.0 opcjonalnych parametrów są już dostępne, co zmniejsza potrzebę takiego podejścia.

Alternatywnym sposobem ponownego użycia kodu w konstruktorze jest wyprowadzenie go na funkcję statyczną, która jest wywoływana z każdego konstruktora, który chce z niego korzystać.

5

Poszukaj "łańcuchów konstruktora w języku C#". Zasadniczo wygląda to następująco:

MyClass():base() //default constructor calling superclass constructor 
{ 
} 

MyClass(int arg):this() //non-basic constructor calling base constructor 
{ 
    //extra initialization 
} 

Pomaga usunąć duplikację kodu w konstruktorze - podzielić je na części podstawowe i szczegółowe.

+0

Dzięki. "Łańcuch konstruktorów" był terminem, którego nie pamiętałem. – JNappi

4

Używasz: base(), gdy chcesz, aby konstruktor klasy podstawowej był automatycznie wywoływany jako pierwsza instrukcja twojego konstruktora. : this() jest podobny, ale wywołuje inny konstruktor na tej samej klasie.

W bazie :() i this(): można przekazywać jako wartości stałych parametrów lub wyrażenie na podstawie parametrów konstruktora.

Wywołanie konstruktora podstawowego jest obowiązkowe, gdy klasa podstawowa nie ma konstruktora domyślnego (takiego, który nie przyjmuje parametrów). Nie znam przypadku, w którym: to() jest obowiązkowe.

public class ABaseClass 
{ 
    public ABaseClass(string s) {} 
} 

public class Foo : AChildClass 
{ 
    public AChildClass(string s) : base(s) {} //base mandatory 
    public AChildClass() : base("default value") {} //base mandatory 
    public AChildClass(string s,int i) : base(s+i) {} //base mandatory 
} 

public class AnotherBaseClass 
{ 
    public ABaseClass(string s) {} 
    public ABaseClass():this("default value") {} //call constructor above 
} 

public class Foo : AnotherChildClass 
{ 
    public AnotherChildClass(string s) : base(s) {} //base optional 

} 
28

Po pierwsze, kiedy są obowiązkowe.

Kiedy klasa Derived pochodzi z klasy Base i Base nie posiada domyślny (bez parametrów) Konstruktor, Derived musi wywołać base() wyraźnie z parametrami.

public class Base { 
    public Base(int i) { } 
} 


public class Derived : Base { 
    // public Derived() { } wouldn't work - what should be given for i? 
    public Derived() : base(7) { } 
    public Derived(int i) : base(i) { } 
} 

Kiedy jest to dobra praktyka? Kiedykolwiek chcesz zadzwonić do innego konstruktora.

Załóżmy, że dodajesz w moim poprzednim przykładzie treść do konstruktorów w języku pochodnym.

public class Derived : Base { 
    // public Derived() { } wouldn't work - what should be given for i? 
    public Derived() : base(7) { 
     Console.WriteLine("The value is " + 7); 
    } 
    public Derived(int i) : base(i) { 
     Console.WriteLine("The value is " + i); 
    } 
} 

Zauważasz tutaj duplikację? Łatwiej jest wywołać konstruktor this().

public class Derived : Base { 
    // public Derived() { } wouldn't work - what should be given for i? 
    public Derived() : this(7) { } 
    public Derived(int i) : base(i) { 
     Console.WriteLine("The value is " + i); 
    } 
} 
+0

Tak więc kolejność wywołań w ostatnim przykładzie byłaby: 'base (7)', a następnie 'Derived (7)', a następnie 'Derived()'. Innymi słowy, część 'this (7)' sama NIE wyzwoli 'base()' (przy założeniu, że istnieje taka metoda bazowa). Czy to jest poprawne? – RayLuo

+1

Poprawnie; konstruktory są zawsze powiązane, a każda klasa wywołuje konstruktory klasy podstawowej. Używając 'this', możesz ponownie użyć innego konstruktora tej klasy (który sam użyje' base', niejawnie bez żadnych argumentów lub jawnie); używając 'base', możesz wybrać, który konstruktor klasy podstawowej ma wywołać. – configurator

21

Zastosowanie base gdy nie ma dziedziczenia, a klasa dominująca już zapewnia funkcje, które próbujesz osiągnąć.

Jeśli chcesz odwołać się do bieżącego elementu (lub self), użyj go w nagłówku/sygnaturze konstruktora, gdy nie chcesz duplikować funkcji, która jest już zdefiniowana w innym konstruktorze.

Zasadniczo stosując bazę i to w nagłówku konstruktor jest, aby utrzymać swój kod DRY, czyniąc ją bardziej linkujących i mniej gadatliwy

Oto absolutnie bezsensowne przykład, ale myślę, że ilustruje ideę pokazujący jak dwie mogą być używane.

class Person 
{ 
    public Person(string name) 
    { 
     Debug.WriteLine("My name is " + name); 
    } 
} 

class Employee : Person 
{ 
    public Employee(string name, string job) 
     : base(name) 
    { 
     Debug.WriteLine("I " + job + " for money."); 
    } 

    public Employee() : this("Jeff", "write code") 
    { 
     Debug.WriteLine("I like cake."); 
    } 
} 

Zastosowanie:

var foo = new Person("ANaimi"); 
// output: 
// My name is ANaimi 

var bar = new Employee("ANaimi", "cook food"); 
// output: 
// My name is ANaimi 
// I cook food for money. 

var baz = new Employee(); 
// output: 
// My name is Jeff 
// I write code for money. 
// I like cake.