Diseño de constructores

Los constructores son métodos especiales utilizados para inicializar los tipos y crear instancias de tipos. Los constructores de tipo se utilizan para inicializar los datos estáticos en un tipo. Common Language Runtime (CLR) llama a un constructor de tipo antes de crear ninguna instancia del tipo. Los constructores de tipos son static (Shared en Visual Basic) y no pueden tomar parámetros. Para crear instancias de un tipo se utilizan constructores de instancia. Los constructores de instancias pueden tomar parámetros, pero no se les exige que lo hagan. Un constructor de instancia sin parámetros se denomina constructor predeterminado.

Las instrucciones siguientes describen los procedimientos recomendados para crear constructores.

Plantéese proporcionar constructores sencillos, idealmente predeterminados. Un constructor sencillo tiene un número muy pequeño de parámetros y todos los parámetros son tipos primitivos o enumeraciones.

Considere utilizar un método de generación estático en lugar de un constructor si la semántica de la operación deseada no se asignan directamente a la construcción de una nueva instancia, o si no parece natural seguir las instrucciones de diseño de constructores.

Utilice parámetros de constructor como accesos directos para establecer las propiedades principales.

Establecer las propiedades utilizando el constructor debería producir idénticos resultados que establecer directamente las propiedades. El ejemplo de código siguiente muestra una clase EmployeeRecord que se puede inicializar llamando a un constructor o estableciendo directamente las propiedades. La clase EmployeeManagerConstructor muestra cómo inicializar un objeto EmployeeRecord utilizando el constructor. La clase EmployeeManagerProperties muestra cómo inicializar un objeto EmployeeRecord mediante las propiedades. La clase Tester muestra esta posibilidad sin tener en cuenta la técnica utilizada, los objetos tienen el mismo estado.

Imports System
Imports System.Collections.ObjectModel
namespace Examples.DesignGuidelines.Constructors

    ' This Class can get its data either by setting 
    ' properties or by passing the data to its constructor.
    Public Class EmployeeRecord

        private employeeIdValue as Integer
        private departmentValue as Integer

        Public Sub New()
        End Sub

        Public Sub New(id as Integer, department as Integer)
            Me.employeeIdValue = id
            Me.departmentValue = department
        End Sub 

        Public Property Department as Integer
            Get 
                Return departmentValue
            End Get
            Set 
                departmentValue = value
            End Set
        End Property

        Public Property EmployeeId as Integer
            Get 
                Return employeeIdValue
            End Get
            Set 
                employeeIdValue = value
            End Set
        End Property

        Public Sub DisplayData()
            Console.WriteLine("{0} {1}", EmployeeId, Department)
        End Sub
    End Class

    ' This Class creates Employee records by passing 
    ' argumemnts to the constructor.
    Public Class EmployeeManagerConstructor
        Dim employees as Collection(Of EmployeeRecord) =  _
            new Collection(Of EmployeeRecord)()

        Public Sub AddEmployee(employeeId as Integer, department as Integer)
            Dim record as EmployeeRecord = new EmployeeRecord(employeeId, department)
            employees.Add(record)
            record.DisplayData()
        End Sub
    End Class

    ' This Class creates Employee records by setting properties.
    Public Class EmployeeManagerProperties
        Dim employees as Collection(Of EmployeeRecord)=  _
        new Collection(Of EmployeeRecord)()
        Public Sub AddEmployee(employeeId as Integer, department as Integer)
            Dim record as EmployeeRecord = new EmployeeRecord()
            record.EmployeeId = employeeId
            record.Department = department
            employees.Add(record)
            record.DisplayData()
        End Sub
    End Class

    Public Class Tester
    ' The following method creates objects with the same state
        ' using the two different approaches.
        Public Shared Sub Main()
            Dim byConstructor as EmployeeManagerConstructor = _
                new EmployeeManagerConstructor()
            byConstructor.AddEmployee(102, 102)

            Dim byProperties as EmployeeManagerProperties = _
                new EmployeeManagerProperties()
            byProperties.AddEmployee(102, 102)
        End Sub
    End Class
End Namespace
using System;
using System.Collections.ObjectModel;
namespace Examples.DesignGuidelines.Constructors
{
    // This class can get its data either by setting 
    // properties or by passing the data to its constructor.
    public class EmployeeRecord
    {
        private int employeeId;
        private int department;

        public EmployeeRecord()
        {
        }
        public EmployeeRecord(int id, int department)
        {
            this.employeeId = id;
            this.department = department;
        }
        public int Department
        {
            get {return department;}
            set {department = value;}
        }
        public int EmployeeId
        {
            get {return employeeId;}
            set {employeeId = value;}
        }
        public void DisplayData()
        {
            Console.WriteLine("{0} {1}", EmployeeId, Department);
        }
    }
    // This class creates Employee records by passing 
    // argumemnts to the constructor.
    public class EmployeeManagerConstructor
    {
        Collection<EmployeeRecord > employees = new Collection<EmployeeRecord>();

        public void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord record = new EmployeeRecord(employeeId, department);
            employees.Add(record);
            record.DisplayData();
        }
    }
    // This class creates Employee records by setting properties.
    public class EmployeeManagerProperties
    {
    Collection<EmployeeRecord > employees = new Collection<EmployeeRecord>();
        public void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord record = new EmployeeRecord();
            record.EmployeeId = employeeId;
            record.Department = department;
            employees.Add(record);
            record.DisplayData();
        }
    }
    public class Tester
    {
    // The following method creates objects with the same state
        // using the two different approaches.
        public static void Main()
        {
            EmployeeManagerConstructor byConstructor = 
                new EmployeeManagerConstructor();
            byConstructor.AddEmployee(102, 102);

            EmployeeManagerProperties byProperties = 
                new EmployeeManagerProperties();
            byProperties.AddEmployee(102, 102);
        }
    }
}
using namespace System;
using namespace System::Collections::ObjectModel;

namespace Examples { namespace DesignGuidelines { namespace Constructors
{
    // This class can get its data either by setting 
    // properties or by passing the data to its constructor.
    public ref class EmployeeRecord
    {
    private:
        int employeeId;
        int department;

    public:
        EmployeeRecord()
        {
        }

        EmployeeRecord(int id, int department)
        {
            this->employeeId = id;
            this->department = department;
        }

        property int Department
        {
            int get() {return department;}
            void set(int value) {department = value;}
        }

        property int EmployeeId
        {
            int get() {return employeeId;}
            void set(int value) {employeeId = value;}
        }

        void DisplayData()
        {
            Console::WriteLine("{0} {1}", EmployeeId, Department);
        }
    };

    // This class creates Employee records by passing 
    // argumemnts to the constructor.
    public ref class EmployeeManagerConstructor
    {
    private:
        Collection<EmployeeRecord^>^ employees;

    public:
        EmployeeManagerConstructor()
        {
            employees = gcnew Collection<EmployeeRecord^>();
        }

        void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord^ record = gcnew EmployeeRecord(employeeId, department);
            employees->Add(record);
            record->DisplayData();
        }
    };

    // This class creates Employee records by setting properties.
    public ref class EmployeeManagerProperties
    {
    private:
        Collection<EmployeeRecord^>^ employees;

    public:
        EmployeeManagerProperties()
        {
            employees = gcnew Collection<EmployeeRecord^>();
        }

        void AddEmployee(int employeeId, int department)
        {
            EmployeeRecord^ record = gcnew EmployeeRecord();
            record->EmployeeId = employeeId;
            record->Department = department;
            employees->Add(record);
            record->DisplayData();
        }
    };

    public ref class Tester
    {
    // The following method creates objects with the same state
        // using the two different approaches.
    public:
        static void Main()
        {
            EmployeeManagerConstructor^ byConstructor =
                gcnew EmployeeManagerConstructor();
            byConstructor->AddEmployee(102, 102);

            EmployeeManagerProperties^ byProperties =
                gcnew EmployeeManagerProperties();
            byProperties->AddEmployee(102, 102);
        }
    };
}}}

Tenga en cuenta que en estos ejemplos, y en una biblioteca bien diseñada, ambos enfoques crean los objetos en el mismo estado. No importa el enfoque que prefieran utilizar los desarrolladores.

Utilice el mismo nombre para los parámetros de constructor y una propiedad, si los parámetros de constructor se utilizan simplemente para establecer la propiedad. La única diferencia entre tales parámetros y las propiedades debería ser la grafía.

Esta instrucción se muestra en el ejemplo anterior.

Haga el trabajo mínimo en el constructor. Los constructores no deberían hacer muchos otros trabajos que captar los parámetros de constructor. El costo de cualquier otro procesamiento se debería retrasar hasta que fuera necesario.

Inicie las excepciones desde constructores de instancias si es adecuado.

Los constructores deberían iniciar y controlar las excepciones al igual que cualquier método. Específicamente, un constructor no debería detectar y ocultar ninguna excepción que no pueda controlar. Para obtener información adicional sobre las excepciones, vea Instrucciones de diseño de excepciones.

Declare explícitamente el constructor predeterminado público en clases, si se requiere este tipo de constructor.

Definir un constructor predeterminado es un procedimiento recomendado si su clase lo admite. Aun cuando algunos compiladores agregan automáticamente un constructor predeterminado a su clase, agregarlo explícitamente facilita el mantenimiento de código. También garantiza que el constructor predeterminado permanece definido aun cuando el compilador deje de emitirlo porque se agregue un constructor que toma parámetros.

Evite tener constructores predeterminados en estructuras.

Muchos compiladores, incluido el compilador de C#, no admiten constructores sin parámetros en estructuras.

No llame a miembros virtuales en un objeto incluido dentro de sus constructores.

Llamar a un miembro virtual hace que se llame al reemplazo más derivado, independientemente de que se haya llamado al constructor del tipo que define el reemplazo más derivado. El siguiente ejemplo de código muestra este problema. Cuando se ejecuta el constructor de la clase base, llama al miembro de clase derivada, aunque no se haya llamado al constructor de clase derivado. Este ejemplo imprime BadBaseClass para mostrar que el constructor DerivedFromBad no ha actualizado el campo del estado.

Imports System

Namespace Examples.DesignGuidelines.MemberDesign
    Public Class BadBaseClass

        Protected  state as String
        Public Sub New()

            state = "BadBaseClass"
            SetState()
        End Sub
        Public Overridable Sub SetState()
        End Sub
    End Class

    Public Class DerivedFromBad 
        Inherits BadBaseClass
        Public Sub New()

            state = "DerivedFromBad "
        End Sub

        Public Overrides Sub SetState()
            Console.WriteLine(state)
        End Sub
    End Class

    Public Class tester

        Public Shared Sub Main()

            Dim b as DerivedFromBad = new DerivedFromBad()
        End Sub
    End Class
End Namespace
using System;

namespace Examples.DesignGuidelines.MemberDesign
{
    public class BadBaseClass
    {
        protected string state;
        public BadBaseClass()
        {
            state = "BadBaseClass";
            SetState();
        }
        public virtual void SetState()
        {

        }
    }

    public class DerivedFromBad : BadBaseClass
    {
        public DerivedFromBad()
        {
            state = "DerivedFromBad ";
        }
        public override void SetState()
        {
            Console.WriteLine(state);
        }

    }
    public class tester
    {
        public static void Main()
        {
            DerivedFromBad b = new DerivedFromBad();
        }
    }
}
using namespace System;

namespace Examples { namespace DesignGuidelines { namespace MemberDesign
{
    public ref class BadBaseClass
    {
    protected:
        String^ state;

    public:
        BadBaseClass()
        {
            state = "BadBaseClass";
            SetState();
        }

        virtual void SetState()
        {

        }
    };

    public ref class DerivedFromBad : public BadBaseClass
    {
    public:
        DerivedFromBad()
        {
            state = "DerivedFromBad ";
        }

        virtual void SetState() override
        {
            Console::WriteLine(state);
        }

    };

    public ref class tester
    {
    public:
        static void Main()
        {
            DerivedFromBad^ b = gcnew DerivedFromBad();
        }
    };
}}}

Portions Copyright 2005 Microsoft Corporation. Reservados todos los derechos.

Portions Copyright Addison-Wesley Corporation. Reservados todos los derechos.

Para obtener más información sobre las directrices de diseño, consulte “las instrucciones de diseño de Framework: Convenciones, frases realizadas y modelos para libro de bibliotecas reutilizables de .NET” de Krzysztof Cwalina y Brad Abrams, publicados por Addison-Wesley, 2005.

Vea también

Conceptos

Diseño de constructores de tipos

Otros recursos

Instrucciones de diseño de miembros

Instrucciones de diseño para desarrollar bibliotecas de clases