Introduzione ai tipi di record in C#

Un record in C# è una classe o uno struct che fornisce sintassi e comportamento speciali per l'uso dei modelli di dati. Il modificatore record indica al compilatore di sintetizzare i membri utili per i tipi il cui ruolo primario archivia i dati. Questi membri includono un overload di ToString() e membri che supportano l'uguaglianza dei valori.

Quando usare i record

Negli scenari seguenti è opportuno prendere in considerazione l'uso di un record al posto di una classe o di uno struct:

  • Si vuole definire un modello di dati che dipende dall'uguaglianza dei valori.
  • Si vuole definire un tipo per cui gli oggetti non sono modificabili.

Uguaglianza di valori

Per i record, l'uguaglianza dei valori indica che due variabili di un tipo di record sono uguali se i tipi corrispondono e tutti i valori di proprietà e campo corrispondono. Per altri tipi di riferimento, ad esempio le classi, l'uguaglianza significa uguaglianza dei riferimenti. Ovvero, due variabili di un tipo di classe sono uguali se fanno riferimento allo stesso oggetto. I metodi e gli operatori che determinano l'uguaglianza di due istanze di record usano l'uguaglianza dei valori.

Non tutti i modelli di dati funzionano bene con l'uguaglianza dei valori. Ad esempio, Entity Framework Core dipende dall'uguaglianza dei riferimenti per assicurarsi che usi una sola istanza di un tipo di entità per ciò che è concettualmente un'entità. Per questo motivo, i tipi di record non sono appropriati per l'uso come tipi di entità in Entity Framework Core.

Immutabilità

Un tipo non modificabile è uno che impedisce di modificare qualsiasi proprietà o valori di campo di un oggetto dopo la creazione di un'istanza. L'immutabilità può essere utile quando è necessario un tipo thread-safe o a seconda di un codice hash che rimane invariato in una tabella hash. I record forniscono una sintassi concisa per la creazione e l'uso di tipi non modificabili.

L'immutabilità non è appropriata per tutti gli scenari di dati. Entity Framework Core, ad esempio, non supporta l'aggiornamento con tipi di entità non modificabili.

Come i record si differenziano dalle classi e gli struct

La stessa sintassi che dichiara e crea istanze di classi o struct può essere usata con i record. Sostituire semplicemente la parola chiave class con record, o usare record struct invece di struct. Analogamente, la stessa sintassi per esprimere le relazioni di ereditarietà è supportata dalle classi di record. I record differiscono dalle classi nei modi seguenti:

  • È possibile usare parametri posizionali in un costruttore primario per creare e creare un'istanza di un tipo con proprietà non modificabili.
  • Gli stessi metodi e operatori che indicano l'uguaglianza dei riferimenti o la disuguaglianza nelle classi (ad esempio Object.Equals(Object) e ==), indicano uguaglianza di valori o disuguaglianza nei record.
  • È possibile usare un'espressionewith per creare una copia di un oggetto non modificabile con nuovi valori nelle proprietà selezionate.
  • Il metodo ToString di un record crea una stringa formattata che mostra il nome del tipo di un oggetto e i nomi e i valori di tutte le relative proprietà pubbliche.
  • Un record può ereditare da un altro record. Un record non può ereditare da una classe e una classe non può ereditare da un record.

Gli struct di record differiscono dagli struct in quanto il compilatore sintetizza i metodi per l'uguaglianza e ToString. Il compilatore sintetizza un metodo Deconstruct per gli struct di record posizionali.

Il compilatore sintetizza una proprietà init-only pubblica per ogni parametro del costruttore primario in un oggetto record class. In un record struct, il compilatore sintetizza una proprietà pubblica di lettura/scrittura. Il compilatore non crea proprietà per i parametri del costruttore primario in class e struct tipi che non includono il modificatore record.

Esempi

Nell'esempio seguente viene definito un record pubblico che usa parametri posizionali per dichiarare e creare un'istanza di un record. Stampa quindi il nome del tipo e i valori delle proprietà:


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

}

Nell'esempio seguente viene illustrata l'uguaglianza dei valori nei record:

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

Nell'esempio seguente viene illustrato l'uso di un'espressione with per copiare un oggetto non modificabile e modificare una delle proprietà:

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

Per ulteriori informazioni, consultare Record (Riferimenti per C#).

Specifiche del linguaggio C#

Per altre informazioni, vedere la specifica del linguaggio C#. La specifica del linguaggio costituisce il riferimento ufficiale principale per la sintassi e l'uso di C#.