2009-11-29 11 views
182

Je sais que c'est censé être une question super simple, mais j'ai lutté avec le concept depuis quelque temps maintenant. Ma question est, comment pouvez-vous enchaîner les constructeurs en C#? Je suis dans ma première classe de POO, alors je suis en train d'apprendre. Je ne comprends pas comment fonctionne le chaînage de constructeur ou comment l'implémenter, ou même pourquoi c'est mieux que de simplement faire des constructeurs sans chaînage.C# constructeur chaînage? (Comment faire?)

J'apprécierais quelques exemples avec une explication.

Alors comment les chaîner? Je sais avec deux il va:

public SomeClass this: {0} 

public SomeClass 
{ 
    someVariable = 0 
} 

Mais comment le faites-vous avec trois, quatre et ainsi de suite? Encore une fois, je sais que c'est une question de débutant, mais j'ai du mal à comprendre cela et je ne sais pas pourquoi.

Répondre

275

Vous utilisez la syntaxe standard (en utilisant this comme une méthode) pour choisir la surcharge, l'intérieur la classe:

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

alors:

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

A noter également:

  • vous pouvez chaîner aux constructeurs sur le type de base en utilisant base(...)
  • vous pouvez mettre du code supplémentaire dans chaque constructeur
  • la valeur par défaut (si vous ne spécifiez rien) est base()

Pour "pourquoi?":

  • réduction de code (toujours une bonne chose)
  • nécessaire pour appeler une base constructeur par défaut, par exemple:

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

Notez que vous pouvez également utiliser initialiseurs d'objets d'une manière similaire, bien que (sans avoir besoin d'écrire quoi que ce soit):

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

que je fais un peu Enchaînement et doivent poser une question puisque cette réponse a été voté tant. Y at-il un inconvénient à ce que chaque constructeur définisse les propriétés qui lui ont été transmises, puis appelle le constructeur par défaut pour définir les autres? De cette façon, vous ne codifiez pas les valeurs par défaut ('0' et' "" ') dans plus d'un endroit (moins de chance pour une erreur). Par exemple: 'public Foo (int id): this() {this.id = id; } '? Alternativement, je considérais aussi: 'public Foo (int id): this (" ") {this.id = id; } '. Juste à la recherche de la meilleure façon logique de les enchaîner, d'apprécier toutes les pensées. –

+0

Existe-t-il un moyen de manipuler les valeurs d'argument du constructeur à appeler dans le premier constructeur avant l'appel de l'autre constructeur chaîné? – eaglei22

5

Demandez-vous à ce sujet?

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

Ceci est mieux illustré avec un exemple. L'imagerie nous avons une personne de classe

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

Nous avons donc ici un constructeur qui définit des propriétés, et utilise constructeur enchaînant pour vous permettre de créer l'objet avec juste un nom, ou tout simplement un nom et adresse. Si vous créez une instance avec juste un nom, ceci enverra une valeur par défaut, string.Empty jusqu'au nom et à l'adresse, qui enverra ensuite une valeur par défaut pour Postcode jusqu'au constructeur final. Ce faisant, vous réduisez la quantité de code que vous avez écrit.Un seul constructeur contient du code, vous ne vous répétez pas, donc, par exemple, si vous changez Name d'une propriété en un champ interne, vous n'avez besoin de changer qu'un seul constructeur - si vous définissez cette propriété dans les trois constructeurs ce serait trois endroits pour le changer.

54

Je veux juste apporter un point valide à quiconque cherche pour cela. Si vous allez travailler avec les versions .NET antérieures à la version 4.0 (VS2010), sachez que vous devez créer des chaînes de constructeur comme indiqué ci-dessus.

Cependant, si vous restez en 4.0, j'ai de bonnes nouvelles. Vous pouvez maintenant avoir un seul constructeur avec des arguments optionnels! Je vais simplifier l'exemple de classe Foo:

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

Lorsque vous implémentez le constructeur, vous pouvez utiliser les arguments optionnels puisque par défaut ont été définis.

J'espère que vous avez apprécié cette leçon! Je n'arrive pas à croire que les développeurs se plaignent du chaînage de construction et ne puissent pas utiliser les arguments optionnels par défaut depuis 2004/2005! Maintenant, il a fallu longtemps dans le monde du développement, que les développeurs ont peur de l'utiliser, car il ne sera pas rétrocompatible.

+46

Si vous utilisez cette technique, vous devez savoir que les arguments par défaut sont définis au moment de la compilation dans * l'appelant *, et non dans le * callee *. Cela signifie que si vous déployez du code comme celui-ci dans une bibliothèque et qu'une application utilise un constructeur avec des arguments par défaut; vous avez besoin d'une recompilation de l'application en utilisant la bibliothèque si les arguments par défaut changent. Certaines personnes considèrent que les arguments par défaut dans les interfaces publiques sont intrinsèquement dangereux à cause de ce gotcha. – Chuu

+1

Un autre inconvénient avec l'approche des arguments par défaut est que si vous avez par exemple deux arguments par défaut dans le constructeur, vous ne pouvez pas l'appeler uniquement avec le second. Dans l'exemple ici, vous auriez une erreur de compilation pour: 'Foo myFooOne = new Foo (" ");' –

9

J'ai une classe de journal et donc je ne suis pas écrit indiquant les valeurs encore et encore

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

J'espère que l'exemple suivant à faire la lumière sur l'enchaînement du constructeur.
mon cas d'utilisation ici par exemple, vous attendez que l'utilisateur passe un répertoire à votre constructeur , l'utilisateur ne sait pas quel répertoire passer et décide de laisser vous assigner le répertoire par défaut. vous augmentez et attribuez un répertoire par défaut que vous pensez que fonctionnera.

BTW, j'ai utilisé LINQPad pour cet exemple au cas où vous vous demandez ce que * .Dump() est.
acclamations

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

Il y a un autre point important dans Enchaînement constructeur: commande. Pourquoi? Supposons que vous ayez créé un objet au moment de l'exécution par un framework qui attend son constructeur par défaut. Si vous voulez pouvoir transmettre des valeurs tout en ayant la possibilité de transmettre des arguments constructeurs quand vous le voulez, c'est extrêmement utile.

Je pourrais par exemple avoir une variable de sauvegarde qui est définie à une valeur par défaut par mon constructeur par défaut mais qui a la possibilité d'être écrasée.

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

Quelle est l'utilisation de "Constructor Chain"?
Vous l'utilisez pour appeler un constructeur d'un autre constructeur.

Comment mettre en œuvre "Constructor Chain"?
Utilisez le mot-clé ": this (yourProperties)" après la définition du constructeur. par exemple:

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

Pourquoi est-il utile?
Une raison importante est de réduire le codage et la prévention du code en double.tel que le code répété pour l'initialisation de la propriété Supposons que certaines propriétés de la classe doivent être initialisées avec une valeur spécifique (Dans notre exemple, requestDate). Et la classe a 2 ou plusieurs constructeurs. Sans "Constructor Chain", vous devez répéter le code d'initialisation dans tous les constracteurs de la classe.

Comment ça marche? (Ou, Quelle est la séquence d'exécution dans "Chaîne Constructeur")?
Dans l'exemple ci-dessus, la méthode "a" sera exécutée en premier, puis la séquence d'instructions retournera à la méthode "b". En d'autres termes, le code ci-dessus est égal avec ci-dessous:

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