Creación de constructores de clases y creación de instancias de objetos

Completado

Además de las propiedades y métodos de clase, las definiciones de clase incluyen constructores que se usan para inicializar nuevos objetos (instancias de clase).

Constructores de clase

Un constructor de clase es un método con el mismo nombre que su tipo (los métodos de constructor usan el mismo nombre que la clase).

Hay dos tipos de constructores de clase:

  • Constructores de instancias. Los constructores de instancia se usan para crear e inicializar cualquier variable de campo de instancia cuando se crea un objeto.
  • Constructores estáticos. Los constructores estáticos se usan para inicializar los datos estáticos o para realizar una acción determinada que solo se debe realizar una vez. Se llama automáticamente a constructores estáticos antes de que se cree la primera instancia o se haga referencia a cualquier miembro estático.

Los constructores de clase son constructores de instancia de forma predeterminada.

Sintaxis del constructor de instancia

Un constructor de instancia se declara con el mismo nombre que la clase y no incluye un tipo de valor devuelto. La firma del método del constructor puede incluir un modificador de acceso opcional, el nombre del método y su lista de parámetros. Las firmas de método de un constructor no incluyen un tipo de valor devuelto.

En el ejemplo siguiente se muestra un constructor simple para una clase denominada Person:


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }

    // Remaining implementation of Person class.
}

Las clases pueden tener más de un constructor. Cuando una clase tiene más de un constructor, los constructores suelen tomar argumentos diferentes.

En el ejemplo siguiente se muestra una clase denominada Person con dos constructores.


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.
        string name = "John Doe";
        Console.WriteLine($"Person created: {name}");

    }

    public Person(string fName, string lName)
    {
        string name = fName + " " + lName;

        Console.WriteLine($"Person created: {name}");
   }

   // Remaining implementation of Person class.
}

Creación de instancias de objetos mediante constructores de clases

Cuando se crea una instancia de un objeto mediante la palabra clave new, el entorno de ejecución de .NET llama al constructor de instancia asociado en la definición de clase y asigna memoria para el objeto .

En el siguiente fragmento de código, la clase Person define un constructor de instancia simple. La clase Program incluye un método Main que usa el operador new para crear una instancia de Person denominada person1. El tiempo de ejecución invoca al constructor Person inmediatamente después de asignar memoria para el nuevo objeto.


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
    }
}

Constructores con y sin parámetros

Un constructor que no toma parámetros se denomina constructor sin parámetros. El tiempo de ejecución invoca el constructor sin parámetros cuando se crea una instancia de un objeto mediante el operador new y no se proporciona ningún argumento al constructor.

Nota

A menos que la clase sea estática, el compilador de C# asigna a las clases sin constructor un constructor público sin parámetros para habilitar la creación de instancias de clase.

Las clases suelen definir constructores que toman parámetros. Los constructores que toman parámetros deben llamarse mediante el operador new o una instrucción base. Las clases pueden definir uno o varios constructores.

El siguiente fragmento de código muestra una clase denominada Person con tres constructores:


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine("An instance of the Person class is being instantiated without name or age parameters.");
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using a name ({name}) parameter.");
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using name ({name}) and age ({age}) parameters.");
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Jane Doe");
        Person person3 = new Person("John Doe", 30);
    }
}

Clases sin constructores

Si una clase no tiene constructores de instancia explícitos, C# proporciona un constructor sin parámetros que puede usar para crear instancias de una instancia de esa clase, como se muestra en el ejemplo siguiente:


public class Person
{
    public int age;
    public string name = "unknown";
}

class Example
{
    static void Main()
    {
        var person = new Person();
        Console.WriteLine($"Name: {person.name}, Age: {person.age}");
        // Output:  Name: unknown, Age: 0
    }
}

Este constructor inicializa los campos de instancia y las propiedades según los inicializadores correspondientes. Si un campo o propiedad no tiene inicializador, su valor se establece en el valor predeterminado del tipo de propiedad o del campo. Si declara al menos un constructor de instancia en una clase, C# no proporciona un constructor sin parámetros.

Inicialización de datos de clase mediante parámetros de constructor

Los parámetros pasados a un constructor son locales para el constructor. Los parámetros se usan a menudo para inicializar los campos de datos de una clase.

El fragmento de código siguiente muestra una clase denominada Person con constructores que inicializan los campos personName y personAge:


public class Person
{
    public string personName;
    public string personAge;

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = "unknown";
        personAge = "unknown";
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = "unknown";
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Jane Doe");
        Person person3 = new Person("John Doe", 30);

        Console.WriteLine($"Person 1 Name: {person1.personName} Age: {person1.personAge}");
        Console.WriteLine($"Person 2 Name: {person2.personName} Age: {person2.personAge}");
        Console.WriteLine($"Person 3 Name: {person3.personName} Age: {person3.personAge}");
    }
}

En el ejemplo anterior, la clase Person se define con tres constructores. El primer constructor inicializa los campos personName y personAge en "unknown". El segundo constructor inicializa el campo personName al valor pasado en el parámetro name y el campo personAge para "unknown". El tercer constructor inicializa los campos personName y personAge a los valores pasados en los parámetros name y age, respectivamente.

Dado que los campos son públicos, se puede acceder directamente desde el método Main. Cuando se ejecuta el código, se genera la siguiente salida:


Person 1 Name: unknown Age: unknown
Person 2 Name: Jane Doe Age: unknown
Person 3 Name: John Doe Age: 30

Definiciones de cuerpo de expresión

Si un constructor se puede implementar como una sola instrucción, puede usar una definición de cuerpo de expresión para asignar un parámetro a un miembro de clase cuando se implementa el constructor.

Por ejemplo, el siguiente constructor inicializa el campo modelName con el valor pasado al parámetro model:


public class Car
{
    public string modelName;

    public Car(string model) => modelName = model;
    
}

La clase Car tiene un único campo público, modelName, que es de tipo string. El campo modelName está diseñado para almacenar el nombre del modelo de automóvil.

La clase Car también incluye un constructor que toma un único parámetro de cadena denominado model. El constructor usa una definición de cuerpo de expresión (indicada por la sintaxis =>) para inicializar el campo modelName con el valor pasado al parámetro model. Esto significa que cuando se crea una instancia de un nuevo objeto Car, el campo modelName se establece en el valor proporcionado como argumento para el constructor.

Como el término expresión implica, el lado derecho del operador => es una expresión y no se limita a una instrucción de asignación simple. La expresión puede ser cualquier expresión de C# válida que devuelva un valor.

El siguiente fragmento de código muestra cómo implementar una definición de cuerpo de expresión que realiza un cálculo sencillo:


public class Employee
{
    public int Salary;

    public Employee() { }

    public Employee(int annualSalary) => Salary = annualSalary;

    public Employee(int weeklySalary, int numberOfWeeks) => Salary = weeklySalary * numberOfWeeks;
}

Esta clase se puede crear mediante cualquiera de las instrucciones siguientes:


Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);

Constructores estáticos

Un constructor estático se usa para inicializar los datos estáticos o para realizar una acción determinada que solo se debe realizar una vez. Se llama automáticamente antes de que se cree la primera instancia o se haga referencia a cualquier miembro estático. Se llama a un constructor estático como máximo una vez.

El siguiente fragmento de código muestra una versión actualizada de la clase Person que implementa campos estáticos y un constructor estático:


public class Person
{
    public string personName;
    public string personAge;

    // Static field
    public static string defaultName;
    public static string defaultAge;

    // Static constructor
    static Person()
    {
        // Static field initialization
        defaultName = "unknown";
        defaultAge = "unknown";
    }

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = defaultName;
        personAge = defaultAge;
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = defaultAge;
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

La clase Person actualizada tiene dos campos de instancia, personName y personAge, ambos de tipo string. Estos campos almacenan el nombre y la edad de una persona, respectivamente.

La clase también define dos campos estáticos, defaultName y defaultAge, también de tipo string. Los campos estáticos se comparten entre todas las instancias de la clase y solo se inicializan una vez. En este caso, los campos estáticos se usan para proporcionar valores predeterminados para los campos personName y personAge.

El constructor estático static Person() es responsable de inicializar los campos estáticos. Establece defaultName en "desconocido" y defaultAge en "unknown". Se llama automáticamente al constructor estático antes de que se cree cualquier instancia de la clase o se acceda a cualquier miembro estático.

La clase Person incluye tres constructores de instancia:

El constructor sin parámetros public Person() inicializa los campos personName y personAge con los valores de los campos estáticos defaultName y defaultAge. Esto significa que si no se proporciona ningún argumento al crear un objeto Person, se usan los valores predeterminados "desconocidos" para el nombre y la antigüedad.

El constructor public Person(string name) toma un único parámetro, namee inicializa el campo personName con este valor. El campo personAge se inicializa con el valor del campo estático defaultAge. Este constructor permite la creación de un objeto Person con un nombre especificado utilizando la edad predeterminada.

El constructor public Person(string name, int age) toma dos parámetros, name y age. Inicializa el campo personName con el valor del parámetro name y el campo personAge con la representación de cadena del parámetro age. Este constructor permite la creación de un Person objeto con un especificado name y age.

Propiedades de constructores estáticos

Los constructores estáticos tienen las siguientes propiedades:

  • Un constructor estático no toma modificadores de acceso ni tiene parámetros.

  • Una clase solo puede tener un constructor estático.

  • Los constructores estáticos no se pueden heredar ni sobrecargar.

  • No se puede llamar directamente a un constructor estático y solo está diseñado para ser llamado por Common Language Runtime (CLR). Se invoca automáticamente.

  • El usuario no tiene control sobre cuándo se ejecuta el constructor estático en el programa.

  • Se llama automáticamente a un constructor estático. Inicializa la clase antes de crear la primera instancia o se hace referencia a cualquier miembro estático declarado en esa clase (no sus clases base). Un constructor estático se ejecuta antes de un constructor de instancia. Si los inicializadores de variables de campo estáticos están presentes en la clase del constructor estático, se ejecutan en el orden textual en el que aparecen en la declaración de clase. Los inicializadores se ejecutan inmediatamente antes del constructor estático.

  • Si no proporciona un constructor estático para inicializar campos estáticos, todos los campos estáticos se inicializan en su valor predeterminado.

  • Si un constructor estático produce una excepción, el tiempo de ejecución no lo invoca una segunda vez y el tipo permanece sin inicializar durante la vigencia del dominio de aplicación. Normalmente, se produce una excepción de TypeInitializationException cuando un constructor estático no puede crear instancias de un tipo o para una excepción no controlada dentro de un constructor estático. En el caso de los constructores estáticos que no se definen explícitamente en el código fuente, la solución de problemas podría requerir la inspección del código de lenguaje intermedio (IL).

  • La presencia de un constructor estático impide la adición del atributo de tipo BeforeFieldInit. Esto limita la optimización en tiempo de ejecución.

  • Un campo declarado como static readonly solo se puede asignar como parte de su declaración o en un constructor estático. Cuando no se requiere un constructor estático explícito, inicialice campos estáticos en la declaración en lugar de a través de un constructor estático para mejorar la optimización en tiempo de ejecución.

  • El tiempo de ejecución llama a un constructor estático no más de una vez en un solo dominio de aplicación. Esa llamada se realiza en una región bloqueada en función del tipo específico de la clase . No se necesitan mecanismos de bloqueo adicionales en el cuerpo de un constructor estático.

Nota

Aunque no es accesible directamente, se debe documentar la presencia de un constructor estático explícito para ayudar a solucionar problemas de excepciones de inicialización.