2009-11-29 11 views
182

Sé que esto es supuestamente una pregunta muy simple, pero he estado luchando con el concepto desde hace un tiempo. Mi pregunta es, ¿cómo encadenas a los constructores en C#? Estoy en mi primera clase OOP, así que solo estoy aprendiendo. No entiendo cómo funciona el encadenamiento de constructores o cómo implementarlo, o incluso por qué es mejor que simplemente hacer constructores sin encadenar.C# constructor de encadenamiento? (¿Cómo hacerlo?)

Agradecería algunos ejemplos con una explicación.

Entonces, ¿cómo encadenarlos? Sé que con dos va:

public SomeClass this: {0} 

public SomeClass 
{ 
    someVariable = 0 
} 

Pero, ¿cómo lo haces con tres, cuatro y así sucesivamente?

Una vez más, sé que esta es una pregunta para principiantes, pero estoy luchando por entender esto y no sé por qué.

Respuesta

275

utiliza una sintaxis estándar (con la this como un método) para recoger la sobrecarga, dentro la clase:

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

a continuación:

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

Nota también:

  • puede encadenar a los constructores en el tipo base usando base(...)
  • se puede poner código extra en cada constructor
  • el valor por defecto (si no se especifica nada) es base()

Por "¿por qué?":

  • reducción de código (siempre una buena cosa)
  • necesaria para llamar a una base constructor no predeterminado, por ejemplo:

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

Tenga en cuenta que también se puede utilizar inicializadores de objeto de manera similar, aunque (sin necesidad de escribir nada):

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

estoy haciendo un poco de encadenamiento y tienen que hacer una pregunta ya que esta respuesta fue votado tan altamente. ¿Hay alguna desventaja al hacer que cada constructor establezca las propiedades que se le pasaron y luego llamar al constructor predeterminado para establecer las demás? De esta manera, no está codificando los valores predeterminados ('0' y' "" ') en más de un lugar (menos posibilidades de error). Por ejemplo: 'public Foo (int id): this() {this.id = id; } '? Alternativamente, también estaba considerando: 'Foo público (ID int): this (" ") {this.id = id; } ' Solo buscando la mejor forma lógica de encadenarlos, aprecie cualquier pensamiento. –

+0

¿Hay alguna manera de manipular los valores del argumento del llamado constructor en el primer constructor antes de llamar al otro constructor encadenado? – eaglei22

5

¿Está usted preguntando acerca de esto?

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

Esto se ilustra mejor con un ejemplo. Imaging tenemos una persona de clase

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

Así que aquí tenemos un constructor que establece algunas propiedades, y utiliza constructor de encadenamiento para permitirle crear el objeto con sólo un nombre, o simplemente un nombre y dirección. Si crea una instancia con solo un nombre, enviará un valor predeterminado, string.Empty al nombre y la dirección, que luego envía un valor predeterminado para el código postal hasta el constructor final.

Al hacerlo, está reduciendo la cantidad de código que ha escrito.Solo un constructor tiene código, no se está repitiendo, por ejemplo, si cambia Nombre de una propiedad a un campo interno, solo necesita cambiar un constructor, si configurara esa propiedad en los tres constructores. eso sería tres lugares para cambiarlo.

54

Solo quiero mostrar un punto válido a cualquiera que busque esto. Si va a trabajar con versiones .NET anteriores a la 4.0 (VS2010), tenga en cuenta que debe crear cadenas de constructores como se muestra arriba.

Sin embargo, si te vas a quedar en 4.0, tengo buenas noticias. ¡Ahora puede tener un único constructor con argumentos opcionales! Voy a simplificar el ejemplo de la clase 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"); 
} 

Cuando se implementa el constructor, puede utilizar los argumentos opcionales, ya se han establecido valores predeterminados.

¡Espero que hayan disfrutado esta lección! ¡Simplemente no puedo creer que los desarrolladores se hayan quejado sobre el encadenamiento de construcciones y no puedan usar argumentos opcionales predeterminados desde 2004/2005! Ahora ha tardado tanto en el mundo del desarrollo que los desarrolladores tienen miedo de usarlo porque no será compatible con versiones anteriores.

+46

Si usa esta técnica, debe tener en cuenta que los argumentos predeterminados se establecen en tiempo de compilación en * caller *, no en * callee *. Eso significa que si implementa un código como este en una biblioteca y una aplicación utiliza un constructor con argumentos predeterminados; necesita una nueva compilación de la aplicación utilizando la biblioteca si cambian los argumentos predeterminados. Algunas personas consideran que los argumentos predeterminados en las interfaces públicas son intrínsecamente peligrosos debido a esto. – Chuu

+1

Otro inconveniente con el enfoque de argumentos predeterminados es que si tiene, por ejemplo, dos argumentos predeterminados en el constructor, no puede llamarlo solo con el segundo. En el ejemplo aquí, habría compilado el error para: 'Foo myFooOne = new Foo (" ");' –

9

que tienen una clase de diario y por lo que no estoy escribiendo el establecimiento de los valores de una y otra vez

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

espero siguiente ejemplo arrojar algo de luz sobre el encadenamiento de constructor.
mi caso de uso aquí, por ejemplo, está esperando que el usuario pase un directorio a su constructor , el usuario no sabe qué directorio pasar y decide dejar que asigne el directorio predeterminado. usted intensifica y asigna un directorio predeterminado que cree que funcionará.

BTW, utilicé LINQPad para este ejemplo en caso de que se esté preguntando qué es * .Dump().
aplausos

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

Hay otro punto importante en el encadenamiento de constructor: el orden. ¿Por qué? Digamos que un objeto está siendo construido en tiempo de ejecución por un marco que espera que sea el constructor predeterminado. Si desea poder pasar valores mientras todavía tiene la capacidad de pasar los argumentos del constructor cuando lo desee, esto es extremadamente útil.

Podría, por ejemplo, tener una variable de respaldo que mi constructor predeterminado establezca en un valor predeterminado, pero que tenga la capacidad de sobrescribirse.

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

¿Qué es el uso de "Constructor Chain"?
Lo usa para llamar a un constructor desde otro constructor.

¿Cómo se puede implementar "Constructor Chain"?
Utilice la palabra clave ": this (yourProperties)" después de la definición del constructor. por ejemplo:

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

¿Por qué es útil?
Motivo importante es reducir la codificación y la prevención de código duplicado.como el código repetido para inicializar la propiedad Supongamos que alguna propiedad en la clase debe inicializarse con un valor específico (en nuestra muestra, requestDate). Y la clase tiene 2 o más constructores. Sin "Cadena de Constructor", debe repetir el código de inicialización en todos los constractores de clase.

¿Cómo funciona? (O, ¿Qué es la secuencia de ejecución en "Cadena de constructores")?
en el ejemplo anterior, el método "a" se ejecutará primero, y luego la secuencia de instrucciones volverá al método "b". En otras palabras, el código anterior es igual a continuación:

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