2011-09-27 11 views
42

Necesito llamar a un constructor del cuerpo de otro, ¿cómo hacerlo?C# llamar a un constructor del cuerpo de otro

Básicamente

class foo { 
    public foo (int x, int y) 
    { 
    } 

    public foo (string s) 
    { 
     // ... do something 

     // call another constructor 
     this (x, y); // doesn't work 
     foo (x, y); // neither 
    } 
} 
+0

¿Qué tal un constructor como foo pública (int x, int y, s string) –

Respuesta

61

No puede.

Vas a tener que encontrar una manera de cadena de los constructores, como en:

public foo (int x, int y) { } 
public foo (string s) : this(XFromString(s), YFromString(s)) { ... } 

o mover el código de construcción en un método de configuración común, así:

public foo (int x, int y) { Setup(x, y); } 
public foo (string s) 
{ 
    // do stuff 
    int x = XFromString(s); 
    int y = YFromString(s); 
    Setup(x, y); 
} 

public void Setup(int x, int y) { ... } 
+11

Tenga en cuenta que, en general, un método de configuración no podría escribir en las variables 'readonly', pero un constructor puede pasar' readonly' variables como parámetros 'ref' a un método de configuración que luego podría escribir en su' ref 'parámetros a pesar de que son variables de solo lectura. – supercat

+5

El método de instalación debe, en la mayoría de los casos, ser privado –

+0

Mire que en la primera solución 'XFromString' y' YFromString' deben ser métodos estáticos. Esa es mi forma favorita de hacerlo! – AxelWass

6

Para llamar tanto la base y este constructor de la clase de forma explícita es necesario utilizar la sintaxis se indica a continuación (tenga en cuenta que en C# no se puede utilizar para inicializar los campos como en C++):

class foo 
{ 
    public foo (int x, int y) 
    { 
    } 

    public foo (string s) : this(5, 6) 
    { 
     // ... do something 
    } 
} 

// EDIT: Notado que has usado x, y en tu muestra. Por supuesto, los valores dados al invocar ctor de esa manera no pueden basarse en parámetros de otro constructor, sino que deben resolverse de otra manera (no es necesario que sean constantes, como en el ejemplo de código editado anterior). Si x y y se calcula a partir s, puede hacerlo de esta manera:

 
public foo (string s) : this(GetX(s), GetY(s)) 
+2

pequeño comentario: foo pública (cadena s): este (x, y) no funcionará.foo público (cadena s, int x, int y): esto (x, y) compilará –

+0

Por supuesto, mi mal. Fijo. Gracias por señalar eso –

29

this(x, y) es correcto, pero tiene que ser antes del comienzo del cuerpo del constructor:

public Foo(int x, int y) 
{ 
    ... 
} 

public Foo(string s) : this(5, 10) 
{ 
} 

Tenga en cuenta que:

  • Sólo puede cadena a un constructor, ya sea this o base - constructor que puede encadenar a aNOT ella, por supuesto.
  • No se puede encadenar a un constructor después de ejecutando cualquier código en el cuerpo del constructor.
  • No puede usar this dentro de los argumentos del otro constructor, incluidos los métodos de instancia de llamada, pero puede llamar a métodos estáticos.
  • Cualquier inicializador de variable de instancia es ejecutado antes de la llamada encadenada.

Tengo un poco más de información en mi article about constructor chaining.

+0

Eso es especialmente molesto cuando tienes algo como esto: 'clase A {public A (IEnumerable items) {...} public A (MyClass c): this (c.Items) {... }} '. Cualquier verificación nula en el segundo ctor solo se ejecutará después de que se acceda a 'c'. El segundo ctor arrojará un NRE si c es nulo, en lugar del 'ArgumentNullException' preferido. Si desea mantener sus campos de solo lectura, no hay otra posibilidad que implementar la misma lógica en dos ctors. ¿O * es * hay una mejor manera de hacer esto? –

+2

@DanielHilgarth: Sí, hay. Tener un método de extensión genérico que comprueba la nulidad pero devuelve el valor original en caso contrario. Entonces puedes hacer 'esto (c.ThrowIfNull (" c "). Items)' –

+0

Gracias, eso fue lo único que se me ocurrió, también. Solo quería editar mi comentario ... No estaba seguro si es una buena manera, porque no lo he visto hasta ahora ... –

1

He encontré con este problema de una vez o dos a mí mismo ... lo que terminó tener que hacer era extraer la lógica que necesitaba en ese otro constructor en un método private void y llamarlo en ambos lugares.

class foo 
{ 
    private void Initialize(int x, int y) 
    { 
    //... do stuff 
    } 

    public foo(int x, int y) 
    { 
    Initialize(x, y); 
    } 

    public foo(string s_ 
    { 
    // ... do stuff 

    Initialize(x, y) 
    // ... more stuff 
    } 
} 
Cuestiones relacionadas