2009-11-29 25 views
182

Ich weiß, dass das angeblich eine super einfache Frage ist, aber ich kämpfe seit einiger Zeit mit dem Konzept. Meine Frage ist, wie können Sie Konstruktoren in C# verketten? Ich bin in meiner ersten OOP-Klasse, also lerne ich gerade. Ich verstehe nicht, wie Konstruktorverkettung funktioniert oder wie man es implementiert, oder warum es besser ist, als nur Konstruktoren ohne Verkettung zu machen.C# -Konstruktorverkettung? (Wie geht das?)

Ich würde einige Beispiele mit einer Erklärung schätzen.

Also wie ketten sie? Ich weiß, mit zwei geht es:

public SomeClass this: {0} 

public SomeClass 
{ 
    someVariable = 0 
} 

Aber wie macht man es mit drei, vier und so weiter?

Noch einmal, ich weiß, das ist eine Anfängerfrage, aber ich habe Mühe, das zu verstehen, und ich weiß nicht warum.

Antwort

275

Sie verwenden Standardsyntax (this wie ein Verfahren unter Verwendung), um die Überlast zu holen, innen der Klasse:

class Foo { 
    private int id; 
    private string name; 
    public Foo() : this(0, "") { 
    } 
    public Foo(int id, string name) { 
     this.id = id; 
     this.name = name; 
    } 
    public Foo(int id) : this(id, "") { 
    } 
    public Foo(string name) : this(0, name) { 
    } 
} 

dann:

Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc"); 

ist auch zu beachten:

  • können Sie mit demzu Konstruktoren auf dem Basistyp verketten
  • können Sie zusätzlichen Code in jedem Konstruktor setzen
  • den Standard (wenn Sie angeben, etwas nicht) ist base()

Für "Warum?":

  • Code Reduktion (immer eine gute Sache)
  • notwendig einen nicht-Standard-Base-Konstruktor aufzurufen, zum Beispiel:

    SomeBaseType(int id) : base(id) {...} 
    

Beachten Sie, dass Sie auch Objektinitialisierer in ähnlicher Weise verwenden können, wenn auch (ohne etwas zu schreiben zu benötigen):

SomeType x = new SomeType(), y = new SomeType { Key = "abc" }, 
     z = new SomeType { DoB = DateTime.Today }; 
+0

ich einige Verkettungs mache und habe eine Frage zu stellen, da diese Antwort so hoch gewählt wurde. Gibt es einen Nachteil, wenn jeder Konstruktor die Eigenschaften festlegen muss, die an ihn übergeben wurden, und dann den Standardkonstruktor aufrufen, um die anderen festzulegen? Auf diese Weise kodieren Sie keine Standardwerte ('0' und' "") an mehr als einer Stelle (weniger Chance für einen Fehler). Zum Beispiel: 'public Foo (int id): this() {this.id = id; } '? Alternativ erwog ich auch: 'public Foo (int id): this (" ") {this.id = id; } '. Nur auf der Suche nach dem besten logischen Weg, sie zu verketten, schätze jeden Gedanken. –

+0

Gibt es eine Möglichkeit, die Argumentwerte des Konstruktors im ersten Konstruktor zu manipulieren, bevor der andere verkettete Konstruktor aufgerufen wird? – eaglei22

5

Fragst du darüber?

public class VariantDate { 
    public int day; 
    public int month; 
    public int year; 

    public VariantDate(int day) : this(day, 1) {} 

    public VariantDate(int day, int month) : this(day, month,1900){} 

    public VariantDate(int day, int month, int year){ 
    this.day=day; 
    this.month=month; 
    this.year=year; 
    } 

} 
25

Dies wird am besten mit einem Beispiel veranschaulicht. Imaging Wir haben eine Klasse Person

public Person(string name) : this(name, string.Empty) 
{ 
} 

public Person(string name, string address) : this(name, address, string.Empty) 
{ 
} 

public Person(string name, string address, string postcode) 
{ 
    this.Name = name; 
    this.Address = address; 
    this.Postcode = postcode; 
} 

Also hier wir einen Konstruktor haben, die einige Eigenschaften festlegt und verwendet Konstruktor ermöglichen Verkettungs Sie das Objekt mit nur einem Namen zu erstellen, oder einfach nur einen Namen und eine Adresse. Wenn Sie eine Instanz mit nur einem Namen erstellen, wird ein Standardwert, string.Empty, an den Namen und die Adresse gesendet, der dann einen Standardwert für die Postleitzahl an den endgültigen Konstruktor sendet.

Dadurch reduzieren Sie die Menge an Code, den Sie geschrieben haben.Nur ein Konstruktor enthält tatsächlich Code, Sie wiederholen sich nicht selbst. Wenn Sie beispielsweise Name von einer Eigenschaft in ein internes Feld ändern, müssen Sie nur einen Konstruktor ändern, wenn Sie diese Eigenschaft in allen drei Konstruktoren festlegen Das wären drei Orte, an denen man es ändern könnte.

54

Ich möchte nur einen gültigen Punkt für jeden auf der Suche nach diesem Thema. Wenn Sie mit .NET-Versionen vor 4.0 (VS2010) arbeiten, beachten Sie bitte, dass Sie Konstruktorketten wie oben gezeigt erstellen müssen.

Wenn Sie jedoch in 4.0 bleiben, habe ich gute Nachrichten. Sie können jetzt einen einzelnen Konstruktor mit optionalen Argumenten haben! Ich werde die Foo-Klasse Beispiel vereinfachen:

class Foo { 
    private int id; 
    private string name; 

    public Foo(int id = 0, string name = "") { 
    this.id = id; 
    this.name = name; 
    } 
} 

class Main() { 
    // Foo Int: 
    Foo myFooOne = new Foo(12); 
    // Foo String: 
    Foo myFooTwo = new Foo(name:"Timothy"); 
    // Foo Both: 
    Foo myFooThree = new Foo(13, name:"Monkey"); 
} 

Wenn Sie den Konstruktor implementieren, können Sie die optionalen Argumente verwenden, da Standardwerte festgelegt wurden.

Ich hoffe, dir hat diese Lektion gefallen! Ich kann einfach nicht glauben, dass sich Entwickler seit 2004/2005 über die Konstruktverkettung beschweren und nicht in der Lage sind, optionale Standardargumente zu verwenden! Jetzt hat es SO in der Entwicklungswelt so lange gedauert, dass Entwickler Angst haben, es zu benutzen, weil es nicht abwärtskompatibel sein wird.

+46

Wenn Sie diese Technik verwenden, müssen Sie beachten, dass die Standardargumente bei der Kompilierung im * caller * und nicht in * callee * gesetzt sind. Das bedeutet, wenn Sie Code wie diesen in einer Bibliothek bereitstellen und eine Anwendung einen Konstruktor mit Standardargumenten verwendet; Sie benötigen eine erneute Kompilierung der Anwendung mithilfe der Bibliothek, wenn sich die Standardargumente ändern. Einige Leute betrachten Standardargumente in öffentlichen Schnittstellen aufgrund dieser Gefahr als inhärent gefährlich. – Chuu

+1

Ein weiterer Nachteil bei der Verwendung von Standardargumenten besteht darin, dass Sie, wenn Sie beispielsweise zwei Standardargwörter im Konstruktor haben, diese nicht nur mit dem zweiten Argument aufrufen können. Im folgenden Beispiel hätten Sie einen Kompilierfehler für: 'Foo myFooOne = new Foo (" ");' –

9

Ich habe ein Tagebuch Klasse und so schreibe ich nicht die Werte immer wieder

public Diary() { 
    this.Like = defaultLike; 
    this.Dislike = defaultDislike; 
} 

public Diary(string title, string diary): this() 
{ 
    this.Title = title; 
    this.DiaryText = diary; 
} 

public Diary(string title, string diary, string category): this(title, diary) { 
    this.Category = category; 
} 

public Diary(int id, string title, string diary, string category) 
    : this(title, diary, category) 
{ 
    this.DiaryID = id; 
} 
2

Ich hoffe Einstellung folgenden Beispiel etwas Licht auf Konstruktorverkettung vergießen.
mein Anwendungsfall hier, zum Beispiel erwarten Sie, dass Benutzer ein Verzeichnis an Ihren Konstruktor übergeben, der Benutzer nicht weiß, welches Verzeichnis übergeben werden soll und beschließt, Ihnen Standardverzeichnis zuweisen zu lassen. Sie steigen und vergeben ein Standardverzeichnis, das Ihrer Meinung nach funktioniert.

BTW, ich habe LINQPad für dieses Beispiel verwendet, falls Sie sich fragen, was * .Dump() ist.
prost

void Main() 
{ 

    CtorChaining ctorNoparam = new CtorChaining(); 
    ctorNoparam.Dump(); 
    //Result --> BaseDir C:\Program Files (x86)\Default\ 

    CtorChaining ctorOneparam = new CtorChaining("c:\\customDir"); 
    ctorOneparam.Dump();  
    //Result --> BaseDir c:\customDir 
} 

public class CtorChaining 
{ 
    public string BaseDir; 
    public static string DefaultDir = @"C:\Program Files (x86)\Default\"; 


    public CtorChaining(): this(null) {} 

    public CtorChaining(string baseDir): this(baseDir, DefaultDir){} 

    public CtorChaining(string baseDir, string defaultDir) 
    { 
     //if baseDir == null, this.BaseDir = @"C:\Program Files (x86)\Default\" 
     this.BaseDir = baseDir ?? defaultDir; 
    } 
} 
0

Es ist ein weiterer wichtiger Punkt in Konstruktorverkettung: Reihenfolge. Warum? Angenommen, Sie haben ein Objekt, das zur Laufzeit von einem Framework erstellt wird, das seinen Standardkonstruktor erwartet. Wenn Sie in der Lage sein möchten, Werte weiterzugeben, während Sie weiterhin Konstruktor-Segmente übergeben können, ist dies äußerst nützlich.

Ich könnte zum Beispiel eine Hintergrundvariable haben, die von meinem Standardkonstruktor auf einen Standardwert gesetzt wird, aber die Fähigkeit hat, überschrieben zu werden.

public class MyClass 
{ 
    private IDependency _myDependency; 
    MyClass(){ _myDependency = new DefaultDependency(); } 
    MYClass(IMyDependency dependency) : this() { 
    _myDependency = dependency; //now our dependency object replaces the defaultDependency 
    } 
} 
1

Was ist die Nutzung von "Constructor Kette"?
Sie verwenden es zum Aufrufen eines Konstruktors von einem anderen Konstruktor.

Wie kann "Constructor Chain" implementiert werden?
Verwenden Sie das Schlüsselwort ": this (yourProperties)" nach der Definition des Konstruktors. zum Beispiel:

Class MyBillClass 
{ 
    private DateTime requestDate; 
    private int requestCount; 

    public MyBillClass() 
    { 
     /// ===== we naming "a" constructor ===== /// 
     requestDate = DateTime.Now; 
    } 
    public MyBillClass(int inputCount) : this() 
    { 
     /// ===== we naming "b" constructor ===== /// 
     /// ===== This method is "Chained Method" ===== /// 
     this.requestCount= inputCount; 
    } 
} 

Warum ist es sinnvoll?
Wichtiger Grund ist die Reduzierung der Codierung und die Vermeidung von doppeltem Code.wie zum Beispiel wiederholter Code zum Initialisieren der Eigenschaft Angenommen, einige Eigenschaften in der Klasse müssen mit einem bestimmten Wert initialisiert werden (in unserem Beispiel requestDate). Und die Klasse hat 2 oder mehr Konstruktoren. Ohne "Constructor Chain" müssen Sie den Initialisierungscode in allen Programmierern der Klasse wiederholen.

Wie funktioniert es? (Oder: Was ist die Ausführungssequenz in "Constructor Chain")?
Im obigen Beispiel wird die Methode "a" zuerst ausgeführt, und dann kehrt die Befehlssequenz zur Methode "b" zurück. Mit anderen Worten, ist oben Code mit unten gleich:

Class MyBillClass 
{ 
    private DateTime requestDate; 
    private int requestCount; 

    public MyBillClass() 
    { 
     /// ===== we naming "a" constructor ===== /// 
     requestDate = DateTime.Now; 
    } 
    public MyBillClass(int inputCount) : this() 
    { 
     /// ===== we naming "b" constructor ===== /// 
     // ===== This method is "Chained Method" ===== /// 

     /// *** --- > Compiler execute "MyBillClass()" first, And then continue instruction sequence from here 
     this.requestCount= inputCount; 
    } 
}