c# summary - Llama a un constructor de otro.




example generate (8)

Tengo dos constructores que alimentan valores a los campos de solo lectura.

class Sample
{
    public Sample(string theIntAsString)
    {
        int i = int.Parse(theIntAsString);

        _intField = i;
    }

    public Sample(int theInt)
    {
        _intField = theInt;
    }


    public int IntProperty
    {
        get { return _intField; }
    }
    private readonly int _intField;

}

Un constructor recibe los valores directamente, y el otro realiza algún cálculo y obtiene los valores, luego establece los campos.

Ahora aquí está la trampa:

  1. No quiero duplicar el código de configuración. En este caso, solo se establece un campo pero, por supuesto, puede haber más de uno.
  2. Para que los campos sean de solo lectura, debo establecerlos desde el constructor, por lo que no puedo "extraer" el código compartido a una función de utilidad.
  3. No sé cómo llamar a un constructor de otro.

¿Algunas ideas?


Answers

Si lo que desea no se puede lograr satisfactoriamente sin tener la inicialización en su propio método (por ejemplo, porque quiere hacer mucho antes del código de inicialización, o envuélvalo en un intento-finalmente, o lo que sea) puede tener cualquiera o todo los constructores pasan las variables de solo lectura por referencia a una rutina de inicialización, que luego podrá manipularlas a voluntad.

class Sample
{
    private readonly int _intField;
    public int IntProperty
    {
        get { return _intField; }
    }

    void setupStuff(ref int intField, int newValue)
    {
        intField = newValue;
    }

    public Sample(string theIntAsString)
    {
        int i = int.Parse(theIntAsString);
        setupStuff(ref _intField,i);
    }

    public Sample(int theInt)
    {
        setupStuff(ref _intField, theInt);
    }
}

Antes del cuerpo del constructor, use:

: base (parameters)

: this (parameters)

Ejemplo:

public class People: User
{
   public People (int EmpID) : base (EmpID)
   {
      // Add more statements here.
   }
}

Aquí hay un ejemplo que llama a otro constructor, luego verifica la propiedad que ha establecido.

    public SomeClass(int i)
    {
        I = i;
    }

    public SomeClass(SomeOtherClass soc)
        : this(soc.J)
    {
        if (I==0)
        {
            I = DoSomethingHere();
        }
    }

Me gusta esto:

public Sample(string str) : this(int.Parse(str)) {
}

Estoy mejorando la respuesta de Supercat. Supongo que también se puede hacer lo siguiente:

class Sample
{
    private readonly int _intField;
    public int IntProperty
    {
        get { return _intField; }
    }

    void setupStuff(ref int intField, int newValue)
    {
        //Do some stuff here based upon the necessary initialized variables.
        intField = newValue;
    }

    public Sample(string theIntAsString, bool? doStuff = true)
    {
        //Initialization of some necessary variables.
        //==========================================
        int i = int.Parse(theIntAsString);
        // ................
        // .......................
        //==========================================

        if (!doStuff.HasValue || doStuff.Value == true)
           setupStuff(ref _intField,i);
    }

    public Sample(int theInt): this(theInt, false) //"false" param to avoid setupStuff() being called two times
    {
        setupStuff(ref _intField, theInt);
    }
}

El encadenamiento del constructor, es decir, puede usar "Base" para Es una relación y "Este" puede usar para la misma clase, cuando desee llamar a varios Constructores en una sola llamada.

  class BaseClass
{
    public BaseClass():this(10)
    {
    }
    public BaseClass(int val)
    {
    }
}
    class Program
    {
        static void Main(string[] args)
        {
            new BaseClass();
            ReadLine();
        }
    }

Cuando hereda una clase de una clase base, puede invocar el constructor de la clase base creando una instancia de la clase derivada

class sample
{
    public int x;

    public sample(int value)
    {
        x = value;
    }
}

class der : sample
{
    public int a;
    public int b;

    public der(int value1,int value2) : base(50)
    {
        a = value1;
        b = value2;
    }
}

class run 
{
    public static void Main(string[] args)
    {
        der obj = new der(10,20);

        System.Console.WriteLine(obj.x);
        System.Console.WriteLine(obj.a);
        System.Console.WriteLine(obj.b);
    }
}

La salida del programa de ejemplo es

50 10 20

También puede usar this palabra clave para invocar un constructor de otro constructor

class sample
{
    public int x;

    public sample(int value) 
    {
        x = value;
    }

    public sample(sample obj) : this(obj.x) 
    {
    }
}

class run
{
    public static void Main(string[] args) 
    {
        sample s = new sample(20);
        sample ss = new sample(s);

        System.Console.WriteLine(ss.x);
    }
}

La salida de este programa de ejemplo es

20


La principal diferencia entre el constructor y ngOnInit es que ngOnInit es un gancho del ciclo de vida y se ejecuta después del constructor. La plantilla interpolada de componentes y los valores iniciales de entrada no están disponibles en el constructor, pero están disponibles en ngOnInit .

La diferencia práctica es cómo ngOnInit afecta cómo se estructura el código. La mayoría del código de inicialización se puede mover a ngOnInit - siempre que esto no cree condiciones de carrera .

Constructor antipattern

Una cantidad sustancial de código de inicialización hace que el método de construcción sea difícil de ampliar, leer y probar.

Una receta habitual para separar la lógica de inicialización del constructor de clases es moverla a otro método como init :

class Some {
  constructor() {
    this.init();
  }

  init() {...}
}

ngOnInit puede servir este propósito en componentes y directivas:

constructor(
  public foo: Foo,
  /* verbose list of dependencies */
) {
  // time-sensitive initialization code
  this.bar = foo.getBar();
}

ngOnInit() {
  // rest of initialization code
}

Inyección de dependencia

El papel principal de los constructores de clase en Angular es la inyección de dependencia. Los constructores también se utilizan para la anotación DI en TypeScript. Casi todas las dependencias se asignan como propiedades a la instancia de clase.

El componente promedio / constructor de directivas ya es lo suficientemente grande porque puede tener una firma multilínea debido a las dependencias, lo que pone una lógica de intialización innecesaria en el cuerpo del constructor que contribuye al antipattern.

Inicialización asíncrona

El constructor de inicialización asíncrono a menudo se puede considerar antipatrón y tiene olor porque la creación de instancias de clase termina antes que la rutina asíncrona, y esto puede crear condiciones de carrera. Si no es el caso, ngOnInit y otros ganchos del ciclo de vida son mejores lugares para esto, particularmente porque pueden beneficiarse de la sintaxis async :

constructor(
  public foo: Foo,
  public errorHandler: ErrorHandler
) {}

async ngOnInit() {
  try {
    await this.foo.getBar();
    await this.foo.getBazThatDependsOnBar();
  } catch (err) {
    this.errorHandler.handleError(err);
  }
}

Si hay condiciones de carrera (incluida la de que un componente no debería aparecer en el error de inicialización), la rutina de inicialización asíncrona debe realizarse antes de la creación de instancias del componente y se debe mover al componente principal, al protector del enrutador, etc.

Examen de la unidad

ngOnInit es más flexible que un constructor y proporciona algunos beneficios para las pruebas unitarias que se explican en detalle en esta respuesta .

Teniendo en cuenta que ngOnInit no se llama automáticamente en la compilación de componentes en pruebas unitarias, los métodos que se llaman en ngOnInit se pueden espiar o ngOnInit de la ngOnInit instancias de componentes.

En casos excepcionales, ngOnInit puede ser completamente aplastado para proporcionar aislamiento para otras unidades componentes (por ejemplo, alguna lógica de plantilla).

Herencia

Las clases de niños solo pueden aumentar constructores, no reemplazarlos.

Como this no se puede referir antes de super() , esto pone restricciones en la prioridad de inicialización.

Teniendo en cuenta que el componente o la directiva Angular usa ngOnInit para la lógica de inicialización insensible al tiempo, las clases secundarias pueden elegir si se llama a super.ngOnInit() y cuándo:

ngOnInit() {
  this.someMethod();
  super.ngOnInit();
}

Esto sería imposible de implementar solo con el constructor.







c# constructor