Constructores de instancias (guía de programación de C#)

Un constructor de instancia se declara para especificar el código que se ejecuta al crear una instancia de un tipo con la expresión new. Para inicializar una clase estática, o variables estáticas en una clase no estática, puede definir un constructor estático.

Como se muestra en el ejemplo siguiente, puede declarar varios constructores de instancia en un tipo:

class Coords
{
    public Coords()
        : this(0, 0)
    {  }

    public Coords(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; set; }
    public int Y { get; set; }

    public override string ToString() => $"({X},{Y})";
}

class Example
{
    static void Main()
    {
        var p1 = new Coords();
        Console.WriteLine($"Coords #1 at {p1}");
        // Output: Coords #1 at (0,0)

        var p2 = new Coords(5, 3);
        Console.WriteLine($"Coords #2 at {p2}");
        // Output: Coords #2 at (5,3)
    }
}

En el ejemplo anterior, el primer constructor, sin parámetros, llama al segundo constructor con los dos argumentos igual a 0. Para hacerlo, use la palabra clave this.

Al declarar un constructor de instancia en una clase derivada, puede llamar a un constructor de una clase base. Para ello, use la palabra clave base, como se muestra en el ejemplo siguiente:

abstract class Shape
{
    public const double pi = Math.PI;
    protected double x, y;

    public Shape(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public abstract double Area();
}

class Circle : Shape
{
    public Circle(double radius)
        : base(radius, 0)
    {  }

    public override double Area() => pi * x * x;
}

class Cylinder : Circle
{
    public Cylinder(double radius, double height)
        : base(radius)
    {
        y = height;
    }

    public override double Area() => (2 * base.Area()) + (2 * pi * x * y);
}

class Example
{
    static void Main()
    {
        double radius = 2.5;
        double height = 3.0;

        var ring = new Circle(radius);
        Console.WriteLine($"Area of the circle = {ring.Area():F2}");
        // Output: Area of the circle = 19.63
        
        var tube = new Cylinder(radius, height);
        Console.WriteLine($"Area of the cylinder = {tube.Area():F2}");
        // Output: Area of the cylinder = 86.39
    }
}

Constructores sin parámetros

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
    }
}

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

Una estructura tipo siempre proporciona un constructor sin parámetros. El constructor sin parámetros es un constructor sin parámetros implícito que genera el valor predeterminado de un tipo o un constructor sin parámetros declarado explícitamente. Para más información, consulte la sección Inicialización de estructuras y valores predeterminados del artículo Tipos de estructuras.

Constructores principales

A partir de C# 12, puede declarar un constructor principal en clases y structs. Para ello, hay que colocar los parámetros entre paréntesis después del nombre de tipo:

public class NamedItem(string name)
{
    public string Name => name;
}

Los parámetros de un constructor principal están dentro del ámbito de todo el cuerpo del tipo declarativo. Pueden inicializar propiedades o campos, usarse como variables en métodos o funciones locales y pasarse a un constructor base.

Un constructor principal indica que estos parámetros son necesarios en cualquier instancia del tipo. Los constructores escritos explícitamente deben usar la sintaxis this(...) del inicializador para invocar al constructor principal. Así, se garantiza que todos los constructores asignan indiscutiblemente parámetros del constructor principal. En cualquier tipo class (incluidos los tipos record class), el constructor sin parámetros implícito no se emite cuando hay un constructor principal. En cualquier tipo struct (incluidos los tipos record struct), siempre se emite el constructor sin parámetros implícito, y siempre se inicializan todos los campos (incluidos los parámetros del constructor principal) en el patrón de 0 bits. Si se escribe un constructor sin parámetros explícito, este debe invocar al constructor principal. En ese caso, se puede especificar un valor diferente en los parámetros del constructor principal. En el siguiente código se muestran ejemplos de constructores principales.

// name isn't captured in Widget.
// width, height, and depth are captured as private fields
public class Widget(string name, int width, int height, int depth) : NamedItem(name)
{
    public Widget() : this("N/A", 1,1,1) {} // unnamed unit cube

    public int WidthInCM => width;
    public int HeightInCM => height;
    public int DepthInCM => depth;

    public int Volume => width * height * depth;
}

Puede agregar atributos al método de constructor principal sintetizado especificando el destino method: en el atributo :

[method: MyAttribute]
public class TaggedWidget(string name)
{
   // details elided
}

Si no especifica el destino method, el atributo se coloca en la clase en lugar del método.

En los tipos class y struct, los parámetros del constructor principal están disponibles en cualquier parte del cuerpo del tipo. Se pueden usar como campos de miembro. Si se usa un parámetro de constructor principal, el compilador captura el parámetro de constructor en un campo privado con un nombre generado por el compilador. Si no se usa un parámetro de constructor principal en el cuerpo del tipo, no se captura ningún campo privado. Esta norma impide que se asignen por error dos copias de un parámetro de constructor principal que se ha pasado a un constructor base.

Si el tipo incluye el modificador record, el compilador sintetiza en su lugar una propiedad pública con el mismo nombre que el parámetro de constructor principal. En los tipos record class, si un parámetro de constructor principal usa el mismo nombre que un constructor principal base, esa propiedad es una propiedad pública del tipo base record class. No se duplica en el tipo derivado record class. Estas propiedades no se generan con los tipos que no son record.

Consulte también