Pregunta ¿Hay algún beneficio o inconveniente cuando un constructor implementa otro?


Si tengo una clase como esta:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    public Foo()
    {
        Bars = new List<Bar>();
    }
}

En algún momento, vuelvo a factorizar la clase y agrego un constructor secundario que implementa el primero como este:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    // some more properties were added

    public Foo()
    {
        Bars = new List<Bar>();
    }

    public Foo(string parameter): this()
    {
        .... some code here
    }
}

También podría haber escrito algo similar a esto:

public class Foo
{
    public IEnumerable<Bar> Bars { get; set; }

    // some more properties were added too

    public Foo()
    {
        InitilizeFoo();
    }

    public Foo(string parameter)
    {
        InitilizeFoo();
        .... some code here
    }

    private void InitializeFoo()
    {
        Bars = new List<Bar>();
    }
}

Al ver que ambos enfoques funcionan en este escenario, ¿hay algún beneficio o desventaja en el uso de uno sobre el otro?

¿Es más eficiente la herencia de los conscriptores y hace que el código se ejecute más rápido o hay un inconveniente que desconozco de hacer que la segunda implementación sea más eficiente?


18
2018-05-31 17:19


origen


Respuestas:


Una de las ventajas clave de tener un constructor que llame a otro constructor es que puede establecer campos de solo lectura de esa manera, no puede hacerlo llamando a un método que no sea de constructor.

Por ejemplo:

public class Foo
{
    private readonly int myNumber;

    public Foo() : this(42)
    {
    }

    public Foo(int num)
    {
        myNumber = num;
    }
}

En cuanto al rendimiento, probablemente no sea más o menos eficiente llamar a otro constructor que llamar a otro método, pero es más legible, en mi opinión, para un constructor llamar a otro constructor que llamar a un método privado separado cuyo único punto es ser llamado por un constructor.

Podría haber, por supuesto, situaciones en las que tener un método separado tenga sentido, y ciertamente no es "incorrecto" per se. El encadenamiento de constructores simplemente se lee mejor para muchos para la mayoría de los usos, y no hay un impacto negativo en el rendimiento.

ACTUALIZAR: Realicé 10,000,000 iteraciones de cada camino (método de inicialización encadenado vs privado) y los resultados fueron tan cercanos que fueron casi indistinguibles:

Initializer Method took: 84 ms for 10,000,000 iterations, 8.4E-06 ms/each.
Chained Constructors took: 81 ms for 10,000,000 iterations, 8.1E-06 ms/each.

Entonces, en realidad, desde el punto de vista del desempeño, casi no hay ningún beneficio en ambos sentidos. El principal beneficio es con constructores encadenados que puede configurar readonly campos, y en la mayoría de los casos es más legible.


28
2018-05-31 17:21



Encadenar constructores es una buena manera de aplicar SRP y flujo de programa. Ocultando el código de inicialización dentro de una versión independiente Initialize() la función podría tener sentido si hay otras situaciones en el ciclo de vida del objeto donde también podría querer "Inicializarlo"; quizás si quisieras poder crear instancias rápidas e iniciarlas de forma lenta. Pero si el único momento válido en el ciclo de vida para ejecutar esa funcionalidad es durante la creación de instancias, y la inicialización es un conjunto bien definido de pasos discretos que deben tomarse en orden, el encadenamiento facilita eso.


6
2018-05-31 17:23



La clave es reducir la cantidad de código duplicado. En este caso, llamar al constructor base desde un constructor parametrizado reduce las posibilidades de agregar un error más tarde después de olvidarse de actualizar ambos.


4
2018-05-31 17:22



Un beneficio de tener una función Initialise () es en caso de que desee reiniciar su objeto; simplemente puede llamar a la función init nuevamente en lugar de eliminar y recrear el objeto.


3
2018-05-31 17:23



Me puede quemar por decir esto, pero prefiero usar parámetros predeterminados en este caso:

public Foo(string parameter = null)
{

}

He tenido casos en los que tenía de 10 a 15 parámetros opcionales y tener 15 constructores diferentes no era una solución elegante en mi opinión. Creo que los parámetros predeterminados solo se reintrodujeron en el marco 4.0.


2
2018-05-31 17:26