Introducción a los tipos de registro en C #

Un registro en C# es una clase o estructura que proporciona sintaxis y comportamiento especiales para trabajar con modelos de datos. El modificador record indica al compilador que sintetice los miembros que son útiles para los tipos cuyo rol principal es almacenar datos. Estos miembros incluyen una sobrecarga de ToString() y miembros que admiten la igualdad de valores.

Cuándo se usan los registros

Considere la posibilidad de usar un registro en lugar de una clase o estructura en los escenarios siguientes:

  • Desea definir un modelo de datos que dependa de la igualdad de valores.
  • Desea definir un tipo para el que los objetos son inmutables.

Igualdad de valores

En el caso de los registros, la igualdad de valores significa que dos variables de un tipo de registro son iguales si los tipos coinciden y todos los valores de propiedad y campo coinciden. Para otros tipos de referencia, como las clases, la igualdad significa igualdad de referencias. Es decir, dos variables de un tipo de clase son iguales si hacen referencia al mismo objeto. Los métodos y operadores que determinan la igualdad de dos instancias de registro usan la igualdad de valores.

No todos los modelos de datos funcionan bien con la igualdad de valores. Por ejemplo, Entity Framework Core depende de la igualdad de referencias para garantizar que solo usa una instancia de un tipo de entidad para lo que es conceptualmente una entidad. Por esta razón, los tipos de registro no son adecuados para su uso como tipos de entidad en Entity Framework Core.

Inmutabilidad

Un tipo inmutable es aquél que impide cambiar cualquier valor de propiedad o campo de un objeto una vez creada su instancia. La inmutabilidad puede ser útil cuando se necesita que un tipo sea seguro para los subprocesos o si depende de que un código hash quede igual en una tabla hash. Los registros proporcionan una sintaxis concisa para crear y trabajar con tipos inmutables.

La inmutabilidad no es adecuada para todos los escenarios de datos. Por ejemplo, Entity Framework Core no admite la actualización con tipos de entidad inmutables.

Diferencias entre los registros y las clases y estructuras

La misma sintaxis que declara y crea instancias de clases o estructuras se puede usar con los registros. Basta con sustituir la palabra clave class por record, o bien usar record struct en lugar de struct. Del mismo modo, las clases de registro admiten la misma sintaxis para expresar las relaciones de herencia. Los registros se diferencian de las clases de las siguientes maneras:

  • Puede usar parámetros posicionales en un constructor principal para crear un tipo y sus instancias con propiedades inmutables.
  • Los mismos métodos y operadores que indican la igualdad o desigualdad de la referencia en las clases (como Object.Equals(Object) y ==), indican la igualdad o desigualdad de valores en los registros.
  • Puede usar una expresión with para crear una copia de un objeto inmutable con nuevos valores en las propiedades seleccionadas.
  • El método ToString de un registro crea una cadena con formato que muestra el nombre de tipo de un objeto y los nombres y valores de todas sus propiedades públicas.
  • Un registro puede heredar de otro registro. Un registro no puede heredar de una clase y una clase no puede heredar de un registro.

Las estructuras de registro se diferencian de las estructuras en que el compilador sintetiza los métodos para la igualdad y ToString. El compilador sintetiza un método Deconstruct para las estructuras de registro posicional.

El compilador sintetiza una propiedad pública de solo inicialización de cada parámetro del constructor principal en un objeto record class. En record struct, el compilador sintetiza una propiedad pública de lectura y escritura. El compilador no crea propiedades para los parámetros del constructor principal en los tipos class y struct que no incluyan el modificador record.

Ejemplos

En el ejemplo siguiente se define un registro público que usa parámetros posicionales para declarar y crear instancias de un registro. A continuación, imprime el nombre de tipo y los valores de propiedad:


public record Person(string FirstName, string LastName);

public static class Program
{
    public static void Main()
    {
        Person person = new("Nancy", "Davolio");
        Console.WriteLine(person);
        // output: Person { FirstName = Nancy, LastName = Davolio }
    }

}

En el ejemplo siguiente se muestra la igualdad de valores en los registros:

public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static class Program
{
    public static void Main()
    {
        var phoneNumbers = new string[2];
        Person person1 = new("Nancy", "Davolio", phoneNumbers);
        Person person2 = new("Nancy", "Davolio", phoneNumbers);
        Console.WriteLine(person1 == person2); // output: True

        person1.PhoneNumbers[0] = "555-1234";
        Console.WriteLine(person1 == person2); // output: True

        Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
    }
}

En el ejemplo siguiente se muestra el uso de una expresión with para copiar un objeto inmutable y cambiar una de las propiedades:

public record Person(string FirstName, string LastName)
{
    public required string[] PhoneNumbers { get; init; }
}

public class Program
{
    public static void Main()
    {
        Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
        Console.WriteLine(person1);
        // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }

        Person person2 = person1 with { FirstName = "John" };
        Console.WriteLine(person2);
        // output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
        Console.WriteLine(person1 == person2); // output: False

        person2 = person1 with { PhoneNumbers = new string[1] };
        Console.WriteLine(person2);
        // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
        Console.WriteLine(person1 == person2); // output: False

        person2 = person1 with { };
        Console.WriteLine(person1 == person2); // output: True
    }
}

Para obtener más información, consulte Registros (Referencia de C#).

Especificación del lenguaje C#

Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es la fuente definitiva de la sintaxis y el uso de C#.