Comparteix a través de


Estructuras de C#

Sugerencia

¿No está familiarizado con el desarrollo de software? Comience primero con los tutoriales de introducción . Encontrarás structs cuando necesites tipos de valor ligeros en tu código.

¿Competente en otro idioma? Los structs de C# son tipos de valor similares a los structs en C++ o Swift, pero residen en el heap administrado cuando están encapsulados (boxed) y admiten interfaces, constructores y métodos. Eche un vistazo a la sección estructuras de solo lectura para patrones específicos de C#. Para las estructuras de registro, consulte Registros.

Un struct es un tipo de valor que contiene sus datos directamente en la instancia, en lugar de a través de una referencia a un objeto en el montón. Al asignar una estructura a una nueva variable, el tiempo de ejecución copia toda la instancia. Los cambios en una variable no afectan al otro porque cada variable representa una instancia diferente. Use estructuras para tipos pequeños y ligeros cuyo rol principal almacena datos en lugar de modelar el comportamiento. Entre los ejemplos se incluyen coordenadas, colores, medidas o opciones de configuración.

Cuándo usar estructuras

Use una estructura cuando el tipo:

  • Representa un valor único o un pequeño grupo de valores relacionados (aproximadamente 16 bytes o menos).
  • Tiene semántica de valor: dos instancias con los mismos datos deben ser iguales.
  • Es principalmente un contenedor de datos en lugar de un modelo de comportamiento.
  • No necesita herencia de un tipo base (las estructuras no pueden heredar de otras estructuras o clases, pero pueden implementar interfaces).

Para obtener una comparación más amplia que incluya clases, registros, tuplas e interfaces, vea Elegir qué tipo de tipo.

Declarar una estructura

Defina una estructura con la struct palabra clave . Una estructura puede contener campos, propiedades, métodos y constructores, al igual que una clase:

struct Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public readonly double DistanceTo(Point other)
    {
        var dx = X - other.X;
        var dy = Y - other.Y;
        return Math.Sqrt(dx * dx + dy * dy);
    }

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

La Point estructura almacena dos double valores y proporciona un método para calcular la distancia entre dos puntos. El DistanceTo método está marcado readonly porque no modifica el estado de la estructura. Ese patrón se cubre en miembros de solo lectura.

Semántica de valores

Las estructuras son tipos de valor. La asignación copia los datos, por lo que cada variable contiene su propia copia independiente:

var p1 = new Point { X = 3, Y = 4 };
var p2 = p1; // copies the data
p2.X = 10;

Console.WriteLine(p1); // (3, 4)  — p1 is unchanged
Console.WriteLine(p2); // (10, 4) — only p2 was modified

Dado que las estructuras son contenedores de datos, la asignación copia todos los miembros de datos en una nueva instancia independiente. Cada copia es distinta. La modificación de una no afecta a la otra. Este comportamiento difiere de las clases, donde la asignación copia solo la referencia y ambas variables comparten el mismo objeto. Para obtener más información sobre la distinción, vea Tipos de valor y tipos de referencia.

Constructores de estructura

Puede definir constructores en estructuras de la misma manera que en las clases. Las estructuras pueden tener constructores sin parámetros que establezcan valores predeterminados personalizados . El término "constructor sin parámetros" distingue una instancia creada con new (que ejecuta la lógica del constructor) de una instancia predeterminada creada con la default expresión (que inicializa cero todos los campos):

struct ConnectionSettings
{
    public string Host { get; set; }
    public int Port { get; set; }
    public int MaxRetries { get; set; }

    public ConnectionSettings()
    {
        Host = "localhost";
        Port = 8080;
        MaxRetries = 3;
    }
}

Un constructor sin parámetros se ejecuta cuando se usa new sin argumentos. La default expresión omite el constructor y establece todos los campos en sus valores predeterminados (0, null, false). Tenga en cuenta la diferencia:

var custom = new ConnectionSettings();
Console.WriteLine($"{custom.Host}:{custom.Port} (retries: {custom.MaxRetries})");
// localhost:8080 (retries: 3)

var defaults = default(ConnectionSettings);
Console.WriteLine($"{defaults.Host ?? "(null)"}:{defaults.Port} (retries: {defaults.MaxRetries})");
// (null):0 (retries: 0)

El compilador inicializa automáticamente los campos que no se establecen explícitamente en un constructor. Solo puede inicializar los campos que necesitan valores no predeterminados:

struct GameTile
{
    public int Row { get; set; }
    public int Column { get; set; }
    public bool IsBlocked { get; set; }

    public GameTile(int row, int column)
    {
        Row = row;
        Column = column;
        // IsBlocked is automatically initialized to false
    }
}

En el ejemplo siguiente se muestra el valor predeterminado de IsBlocked:

var tile = new GameTile(2, 5);
Console.WriteLine($"Tile ({tile.Row}, {tile.Column}), blocked: {tile.IsBlocked}");
// Tile (2, 5), blocked: False

La IsBlocked propiedad no está asignada en el constructor, por lo que el compilador la establece en false (el valor predeterminado para bool). Esta característica reduce el código repetitivo en los constructores que solo necesitan asignar algunos campos.

Estructuras de solo lectura y miembros de solo lectura

readonly struct garantiza que ningún miembro de instancia modifique el estado del struct. El compilador aplica esta garantía al exigir que todos los campos y las propiedades implementadas automáticamente sean de solo lectura:

readonly struct Temperature
{
    public double Celsius { get; }

    public Temperature(double celsius) => Celsius = celsius;

    public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;

    public override string ToString() => $"{Celsius:F1}°C ({Fahrenheit:F1}°F)";
}

En el siguiente ejemplo, se crea una instancia de Temperature y se leen sus propiedades.

var temp = new Temperature(100);
Console.WriteLine(temp); // 100.0°C (212.0°F)
// temp.Celsius = 50; // Error: property is read-only

Cuando no necesite que toda la estructura sea inmutable, marque miembros individuales como readonly en su lugar. Un readonly miembro no puede modificar el estado del struct y el compilador comprueba esa garantía:

struct Velocity
{
    public double X
    {
        readonly get;
        set;
    }

    public double Y
    {
        readonly get;
        set;
    }

    public readonly double Speed => Math.Sqrt(X * X + Y * Y);

    public readonly override string ToString() => $"({X}, {Y}) speed={Speed:F2}";
}

En el ejemplo siguiente se muestra que readonly los miembros devuelven valores actualizados cuando cambian las propiedades mutables:

var v = new Velocity { X = 3, Y = 4 };
Console.WriteLine(v.Speed); // 5
Console.WriteLine(v);       // (3, 4) speed=5.00
v.X = 6;
Console.WriteLine(v.Speed); // 7.211...

La marcación de miembros readonly ayuda al compilador a optimizar las copias defensivas. Cuando se pasa una readonly estructura a un método que acepta un in parámetro, el compilador sabe que no se necesita ninguna copia.

Consulte también