Créer des types d’enregistrementsCreate record types

C# 9 introduit les enregistrements, un nouveau type de référence que vous pouvez créer à la place des classes ou des structs.C# 9 introduces records, a new reference type that you can create instead of classes or structs. Les enregistrements sont distincts des classes dans les types d’enregistrements utilisant l' égalité basée sur les valeurs.Records are distinct from classes in that record types use value-based equality. Deux variables d’un type d’enregistrement sont égales si les définitions de type d’enregistrement sont identiques et, si pour chaque champ, les valeurs des deux enregistrements sont égales.Two variables of a record type are equal if the record type definitions are identical, and if for every field, the values in both records are equal. Deux variables d’un type de classe sont égales si les objets référencés sont du même type de classe et que les variables font référence au même objet.Two variables of a class type are equal if the objects referred to are the same class type and the variables refer to the same object. L’égalité basée sur la valeur implique d’autres fonctionnalités que vous souhaiterez probablement dans les types d’enregistrements.Value-based equality implies other capabilities you'll probably want in record types. Le compilateur génère un grand nombre de ces membres lorsque vous déclarez un record au lieu d’un class .The compiler generates many of those members when you declare a record instead of a class.

Ce didacticiel vous montre comment effectuer les opérations suivantes :In this tutorial, you'll learn how to:

  • Déterminez si vous devez déclarer un class ou un record .Decide if you should declare a class or a record.
  • Déclarez les types d’enregistrements et les types d’enregistrements positionnels.Declare record types and positional record types.
  • Remplacez les méthodes générées par le compilateur dans les enregistrements par vos méthodes.Substitute your methods for compiler generated methods in records.

PrérequisPrerequisites

Vous devez configurer votre ordinateur pour exécuter .NET 5 ou une version ultérieure, y compris le compilateur C# 9,0 ou version ultérieure.You’ll need to set up your machine to run .NET 5 or later, including the C# 9.0 or later compiler. Le compilateur C# 9,0 est disponible à partir de Visual Studio 2019 version 16,8 ou du Kit de développement logiciel (SDK) .net 5,0.The C# 9.0 compiler is available starting with Visual Studio 2019 version 16.8 or the .NET 5.0 SDK.

Caractéristiques des enregistrementsCharacteristics of records

Vous définissez un enregistrement en déclarant un type avec le record mot clé, au lieu du class struct mot clé ou.You define a record by declaring a type with the record keyword, instead of the class or struct keyword. Un enregistrement est un type référence et suit la sémantique d’égalité basée sur les valeurs.A record is a reference type and follows value-based equality semantics. Pour appliquer la sémantique de valeur, le compilateur génère plusieurs méthodes pour votre type d’enregistrement :To enforce value semantics, the compiler generates several methods for your record type:

En outre, les enregistrements fournissent une substitution de Object.ToString() .In addition, records provide an override of Object.ToString(). Le compilateur synthétise les méthodes pour afficher les enregistrements à l’aide de Object.ToString() .The compiler synthesizes methods for displaying records using Object.ToString(). Vous allez explorer ces membres au fur et à mesure que vous écrirez le code pour ce didacticiel.You'll explore those members as you write the code for this tutorial. Enregistre les with expressions de prise en charge pour permettre une mutation non destructrice des enregistrements.Records support with expressions to enable non-destructive mutation of records.

Vous pouvez également déclarer des enregistrements positionnels à l’aide d’une syntaxe plus concise.You can also declare positional records using a more concise syntax. Le compilateur synthétise davantage de méthodes lorsque vous déclarez des enregistrements positionnels :The compiler synthesizes more methods for you when you declare positional records:

  • Constructeur principal dont les paramètres correspondent aux paramètres positionnels de la déclaration d’enregistrement.A primary constructor whose parameters match the positional parameters on the record declaration.
  • Propriétés public init-only pour chaque paramètre d’un constructeur principal.Public init-only properties for each parameter of a primary constructor.
  • DeconstructMéthode permettant d’extraire des propriétés de l’enregistrement.A Deconstruct method to extract properties from the record.

Données de température de buildBuild temperature data

Les données et les statistiques font partie des scénarios dans lesquels vous souhaiterez utiliser des enregistrements.Data and statistics are among the scenarios where you'll want to use records. Pour ce didacticiel, vous allez créer une application qui calcule le degré de jours pour différentes utilisations.For this tutorial, you'll build an application that computes degree days for different uses. Les jours de degré sont une mesure de chaleur (ou de l’absence de chaleur) sur une période de jours, de semaines ou de mois.Degree days are a measure of heat (or lack of heat) over a period of days, weeks, or months. Degré de suivi et prédiction de l’utilisation de l’énergie.Degree days track and predict energy usage. Plus de plus chaud jours signifie plus de climatisation, et plus de jours plus froids signifient une utilisation plus grande du four.More hotter days means more air conditioning, and more colder days means more furnace usage. Degree Days aident à gérer les populations d’usines.Degree days help manage plant populations. Les jours sont corrélés à la croissance de la plante au fur et à mesure que les saisons évoluent.Degree days correlate to plant growth as the seasons change. Le degree Days aide à effectuer le suivi des migrations animales pour les espèces qui voyagent pour correspondre au climat.Degree days help track animal migrations for species that travel to match climate.

La formule est basée sur la température moyenne d’un jour donné et sur une température de ligne de base.The formula is based on the mean temperature on a given day and a baseline temperature. Pour calculer le degré des jours dans le temps, vous avez besoin des températures haute et basse chaque jour pendant une période donnée.To compute degree days over time, you'll need the high and low temperature each day for a period of time. Commençons par créer une nouvelle application.Let's start by creating a new application. Créez une nouvelle application console.Make a new console application. Créez un nouveau type d’enregistrement dans un nouveau fichier nommé « DailyTemperature.cs » :Create a new record type in a new file named "DailyTemperature.cs":

public record DailyTemperature(double HighTemp, double LowTemp);

Le code précédent définit un enregistrement positionnel.The preceding code defines a positional record. Vous avez créé un type référence qui contient deux propriétés : HighTemp , et LowTemp .You've created a reference type that contains two properties: HighTemp, and LowTemp. Ces propriétés sont des Propriétés init uniquement, ce qui signifie qu’elles peuvent être définies dans le constructeur ou à l’aide d’un initialiseur de propriété.Those properties are init only properties, meaning they can be set in the constructor or using a property initializer. Le DailyTemperature type a également un constructeur principal qui a deux paramètres qui correspondent aux deux propriétés.The DailyTemperature type also has a primary constructor that has two parameters that match the two properties. Vous utilisez le constructeur principal pour initialiser un DailyTemperature enregistrement :You use the primary constructor to initialize a DailyTemperature record:

private static DailyTemperature[] data = new DailyTemperature[]
{
    new DailyTemperature(HighTemp: 57, LowTemp: 30), 
    new DailyTemperature(60, 35),
    new DailyTemperature(63, 33),
    new DailyTemperature(68, 29),
    new DailyTemperature(72, 47),
    new DailyTemperature(75, 55),
    new DailyTemperature(77, 55),
    new DailyTemperature(72, 58),
    new DailyTemperature(70, 47),
    new DailyTemperature(77, 59),
    new DailyTemperature(85, 65),
    new DailyTemperature(87, 65),
    new DailyTemperature(85, 72),
    new DailyTemperature(83, 68),
    new DailyTemperature(77, 65),
    new DailyTemperature(72, 58),
    new DailyTemperature(77, 55),
    new DailyTemperature(76, 53),
    new DailyTemperature(80, 60),
    new DailyTemperature(85, 66) 
};

Vous pouvez ajouter vos propres propriétés ou méthodes à des enregistrements, y compris des enregistrements positionnels.You can add your own properties or methods to records, including positional records. Vous devez calculer la température moyenne pour chaque jour.You'll need to compute the mean temperature for each day. Vous pouvez ajouter cette propriété à l' DailyTemperature enregistrement :You can add that property to the DailyTemperature record:

public record DailyTemperature(double HighTemp, double LowTemp)
{
    public double Mean => (HighTemp + LowTemp) / 2.0;
}

Vérifions que vous pouvez utiliser ces données.Let's make sure you can use this data. Ajoutez le code suivant à votre méthode Main :Add the following code to your Main method:

foreach (var item in data)
    Console.WriteLine(item);

Exécutez votre application. vous verrez une sortie similaire à l’affichage suivant (plusieurs lignes supprimées pour l’espace) :Run your application, and you'll see output that looks similar to the following display (several rows removed for space):

DailyTemperature { HighTemp = 57, LowTemp = 30, Mean = 43.5 }
DailyTemperature { HighTemp = 60, LowTemp = 35, Mean = 47.5 }


DailyTemperature { HighTemp = 80, LowTemp = 60, Mean = 70 }
DailyTemperature { HighTemp = 85, LowTemp = 66, Mean = 75.5 }

Le code précédent montre la sortie de la substitution de ToString synthétisé par le compilateur.The preceding code shows the output from the override of ToString synthesized by the compiler. Si vous préférez du texte différent, vous pouvez écrire votre propre version de ToString .If you prefer different text, you can write your own version of ToString. Cela empêche le compilateur de synthétiser une version pour vous.That prevents the compiler from synthesizing a version for you.

Degré de calcul en joursCompute degree days

Pour calculer les jours, vous prenez la différence par rapport à une température de référence et la température moyenne d’un jour donné.To compute degree days, you take the difference from a baseline temperature and the mean temperature on a given day. Pour mesurer la chaleur dans le temps, vous supprimez tous les jours où la température moyenne est inférieure à la ligne de base.To measure heat over time, you discard any days where the mean temperature is below the baseline. Pour mesurer le froid au fil du temps, vous devez ignorer les jours où la température moyenne est supérieure à la ligne de base.To measure cold over time, you discard any days where the mean temperature is above the baseline. Par exemple, les États-Unis utilisent 65F comme base pour les jours de chauffage et de refroidissement.For example, the U.S. uses 65F as the base for both heating and cooling degree days. Il s’agit de la température à laquelle aucun chauffage ou refroidissement n’est nécessaire.That's the temperature where no heating or cooling is needed. Si un jour a une température moyenne de 70F, ce jour est de 5 jours de refroidissement et 0 jour de chauffage.If a day has a mean temperature of 70F, that day is 5 cooling degree days and 0 heating degree days. À l’inverse, si la température moyenne est 55F, ce jour correspond à 10 jours de chauffage et 0 jour de refroidissement.Conversely, if the mean temperature is 55F, that day is 10 heating degree days and 0 cooling degree days.

Vous pouvez exprimer ces formules sous la forme d’une petite hiérarchie de types d’enregistrements : un type de jour abstrait et deux types concrets pour le chauffage des jours et le degré de refroidissement.You can express these formulas as a small hierarchy of record types: an abstract degree day type and two concrete types for heating degree days and cooling degree days. Ces types peuvent également être des enregistrements positionnels.These types can also be positional records. Ils prennent une température de référence et une séquence d’enregistrements de température quotidiens comme arguments pour le constructeur principal :They take a baseline temperature and a sequence of daily temperature records as arguments to the primary constructor:

public abstract record DegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords);

public record HeatingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean < BaseTemperature).Sum(s => BaseTemperature - s.Mean);
}

public sealed record CoolingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean > BaseTemperature).Sum(s => s.Mean - BaseTemperature);
}

L' DegreeDays enregistrement abstrait est la classe de base partagée pour les HeatingDegreeDays CoolingDegreeDays enregistrements et.The abstract DegreeDays record is the shared base class for both the HeatingDegreeDays and CoolingDegreeDays records. Les déclarations de constructeur principales sur les enregistrements dérivés montrent comment gérer l’initialisation des enregistrements de base.The primary constructor declarations on the derived records show how to manage base record initialization. Votre enregistrement dérivé déclare des paramètres pour tous les paramètres dans le constructeur principal d’enregistrement de base.Your derived record declares parameters for all the parameters in the base record primary constructor. L’enregistrement de base déclare et initialise ces propriétés.The base record declares and initializes those properties. L’enregistrement dérivé ne les masque pas, mais crée et initialise uniquement les propriétés des paramètres qui ne sont pas déclarés dans son enregistrement de base.The derived record doesn't hide them, but only creates and initializes properties for parameters that aren't declared in its base record. Dans cet exemple, les enregistrements dérivés n’ajoutent pas de nouveaux paramètres de constructeur principal.In this example, the derived records don't add new primary constructor parameters. Testez votre code en ajoutant le code suivant à votre Main méthode :Test your code by adding the following code to your Main method:

var heatingDegreeDays = new HeatingDegreeDays(65, data);
Console.WriteLine(heatingDegreeDays);

var coolingDegreeDays = new CoolingDegreeDays(65, data);
Console.WriteLine(coolingDegreeDays);

Vous obtenez une sortie similaire à l’affichage suivant :You'll get output like the following display:

HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 }
CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 71.5 }

Définir des méthodes synthétisées par le compilateurDefine compiler-synthesized methods

Votre code calcule le nombre correct de jours de chauffage et de refroidissement pendant cette période.Your code calculates the correct number of heating and cooling degree days over that period of time. Mais cet exemple montre pourquoi vous souhaiterez peut-être remplacer certaines des méthodes synthétisées pour les enregistrements.But this example shows why you may want to replace some of the synthesized methods for records. Vous pouvez déclarer votre propre version de l’une des méthodes synthétisées par le compilateur dans un type d’enregistrement, à l’exception de la méthode Clone.You can declare your own version of any of the compiler-synthesized methods in a record type except the clone method. La méthode Clone a un nom généré par le compilateur et vous ne pouvez pas fournir une implémentation différente.The clone method has a compiler generated name and you cannot provide a different implementation. Ces méthodes synthétisées incluent un constructeur de copie, les membres de l' System.IEquatable<T> interface, les tests d’égalité et d’inégalité, et GetHashCode() .These synthesized methods include a copy constructor, the members of the System.IEquatable<T> interface, equality and inequality tests, and GetHashCode(). À cet effet, vous allez synthétiser PrintMembers .For this purpose, you'll synthesize PrintMembers. Vous pouvez également déclarer votre propre ToString , mais PrintMembers offre une meilleure option pour les scénarios d’héritage.You could also declare your own ToString, but PrintMembers provides a better option for inheritance scenarios. Pour fournir votre propre version d’une méthode synthétisée, la signature doit correspondre à la méthode synthétisée.To provide your own version of a synthesized method, the signature must match the synthesized method.

L' TempRecords élément dans la sortie de la console n’est pas utile.The TempRecords element in the console output isn't useful. Elle affiche le type, mais rien d’autre.It displays the type, but nothing else. Vous pouvez modifier ce comportement en fournissant votre propre implémentation de la méthode synthétisée PrintMembers .You can change this behavior by providing your own implementation of the synthesized PrintMembers method. La signature dépend des modificateurs appliqués à la record déclaration :The signature depends on modifiers applied to the record declaration:

  • Si un type d’enregistrement est sealed , la signature est private bool PrintMembers(StringBuilder builder);If a record type is sealed, the signature is private bool PrintMembers(StringBuilder builder);
  • Si un type d’enregistrement n’est pas sealed et dérive de object (c’est-à-dire qu’il ne déclare pas d’enregistrement de base), la signature est protected virtual bool PrintMembers(StringBuilder builder);If a record type isn't sealed and derives from object (that is, it doesn't declare a base record), the signature is protected virtual bool PrintMembers(StringBuilder builder);
  • Si un type d’enregistrement n’est pas sealed et qu’il dérive d’un autre enregistrement, la signature est protected override bool PrintMembers(StringBuilder builder);If a record type isn't sealed and derives from another record, the signature is protected override bool PrintMembers(StringBuilder builder);

Ces règles sont plus faciles à comprendre pour comprendre l’objectif de PrintMembers .These rules are easiest to comprehend through understanding the purpose of PrintMembers. PrintMembers ajoute à une chaîne des informations sur chaque propriété d’un type d’enregistrement.PrintMembers adds information about each property in a record type to a string. Le contrat requiert des enregistrements de base pour ajouter leurs membres à l’affichage et suppose que les membres dérivés ajouteront leurs membres.The contract requires base records to add their members to the display and assumes derived members will add their members. Chaque type d’enregistrement synthétise un ToString remplacement qui ressemble à l’exemple suivant pour HeatingDegreeDays :Each record type synthesizes a ToString override that looks similar to the following example for HeatingDegreeDays:

public override string ToString()
{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("HeatingDegreeDays");
    stringBuilder.Append(" { ");
    if (PrintMembers(stringBuilder))
    {
        stringBuilder.Append(" ");
    }
    stringBuilder.Append("}");
    return stringBuilder.ToString();
}

Vous déclarez une PrintMembers méthode dans l' DegreeDays enregistrement qui n’imprime pas le type de la collection :You declare a PrintMembers method in the DegreeDays record that doesn't print the type of the collection:

protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
    stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
    return true;
}

La signature déclare une virtual protected méthode pour qu’elle corresponde à la version du compilateur.The signature declares a virtual protected method to match the compiler's version. Ne vous inquiétez pas si vous recevez les accesseurs erronés ; le langage applique la signature correcte.Don't worry if you get the accessors wrong; the language enforces the correct signature. Si vous oubliez les modificateurs corrects pour toute méthode synthétisée, le compilateur émet des avertissements ou des erreurs qui vous aident à obtenir la signature appropriée.If you forget the correct modifiers for any synthesized method, the compiler issues warnings or errors that help you get the right signature.

Mutation non destructriceNon-destructive mutation

Les membres synthétisés dans un enregistrement positionnel ne modifient pas l’état de l’enregistrement.The synthesized members in a positional record don't modify the state of the record. L’objectif est que vous pouvez créer plus facilement des enregistrements immuables.The goal is that you can more easily create immutable records. Examinez à nouveau les déclarations précédentes pour HeatingDegreeDays et CoolingDegreeDays .Look again at the preceding declarations for HeatingDegreeDays and CoolingDegreeDays. Les membres ajoutés effectuent des calculs sur les valeurs de l’enregistrement, mais ne changent pas d’État.The members added perform computations on the values for the record, but don't mutate state. Les enregistrements positionnels facilitent la création de types référence immuables.Positional records make it easier for you to create immutable reference types.

La création de types référence immuables signifie que vous souhaiterez utiliser une mutation non destructrice.Creating immutable reference types means you'll want to use non-destructive mutation. Vous créez des instances d’enregistrement similaires aux instances d’enregistrement existantes à l’aide d' with expressions.You create new record instances that are similar to existing record instances using with expressions. Ces expressions sont une construction de copie avec des affectations supplémentaires qui modifient la copie.These expressions are a copy construction with additional assignments that modify the copy. Le résultat est une nouvelle instance d’enregistrement dans laquelle chaque propriété a été copiée à partir de l’enregistrement existant et éventuellement modifiée.The result is a new record instance where each property has been copied from the existing record and optionally modified. L’enregistrement d’origine est inchangé.The original record is unchanged.

Nous allons ajouter quelques fonctionnalités à votre programme qui illustrent des with expressions.Let's add a couple features to your program that demonstrate with expressions. Tout d’abord, nous allons créer un nouvel enregistrement pour calculer le nombre de jours croissants à l’aide des mêmes données.First, let's create a new record to compute growing degree days using the same data. Le nombre de jours croissants utilise généralement 41F comme ligne de base et mesure les températures au-dessus de la ligne de base.Growing degree days typically uses 41F as the baseline and measures temperatures above the baseline. Pour utiliser les mêmes données, vous pouvez créer un nouvel enregistrement similaire à coolingDegreeDays , mais avec une température de base différente :To use the same data, you can create a new record that is similar to the coolingDegreeDays, but with a different base temperature:

// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);

Vous pouvez comparer le nombre de degrés calculés aux nombres générés à l’aide d’une température de référence supérieure.You can compare the number of degrees computed to the numbers generated with a higher baseline temperature. N’oubliez pas que les enregistrements sont des types référence et que ces copies sont des copies superficielles.Remember that records are reference types and these copies are shallow copies. Le tableau des données n’est pas copié, mais les deux enregistrements font référence aux mêmes données.The array for the data isn't copied, but both records refer to the same data. Ce fait est un avantage dans un autre scénario.That fact is an advantage in one other scenario. Pour des jours de plus en bout, il est utile d’effectuer le suivi du total des 5 jours précédents.For growing degree days, it's useful to keep track of the total for the previous 5 days. Vous pouvez créer des enregistrements avec différentes données sources à l’aide d' with expressions.You can create new records with different source data using with expressions. Le code suivant génère une collection de ces accumulations, puis affiche les valeurs :The following code builds a collection of these accumulations, then displays the values:

// showing moving accumulation of 5 days using range syntax
List<CoolingDegreeDays> movingAccumulation = new();
int rangeSize = (data.Length > 5) ? 5 : data.Length;
for (int start = 0; start < data.Length - rangeSize; start++)
{
    var fiveDayTotal = growingDegreeDays with { TempRecords = data[start..(start + rangeSize)] };
    movingAccumulation.Add(fiveDayTotal);
}
Console.WriteLine();
Console.WriteLine("Total degree days in the last five days");
foreach(var item in movingAccumulation)
{
    Console.WriteLine(item);
}

Vous pouvez également utiliser with des expressions pour créer des copies d’enregistrements.You can also use with expressions to create copies of records. Ne spécifiez aucune propriété entre les accolades de l' with expression.Don't specify any properties between the braces for the with expression. Cela signifie créer une copie et ne pas modifier les propriétés :That means create a copy, and don't change any properties:

var growingDegreeDaysCopy = growingDegreeDays with { };

Exécutez l’application finie pour afficher les résultats.Run the finished application to see the results.

RésuméSummary

Ce didacticiel a présenté plusieurs aspects des enregistrements.This tutorial showed several aspects of records. Les enregistrements fournissent une syntaxe concise pour les types de référence où l’utilisation fondamentale est le stockage des données.Records provide concise syntax for reference types where the fundamental use is storing data. Pour les classes orientées objet, l’utilisation fondamentale est la définition des responsabilités.For object-oriented classes, the fundamental use is defining responsibilities. Ce didacticiel se concentre sur les enregistrements positionnels, où vous pouvez utiliser une syntaxe concise pour déclarer les propriétés d’initialisation uniquement pour un enregistrement.This tutorial focused on positional records, where you can use a concise syntax to declare the init-only properties for a record. Le compilateur synthétise plusieurs membres de l’enregistrement pour copier et comparer des enregistrements.The compiler synthesizes several members of the record for copying and comparing records. Vous pouvez ajouter tous les autres membres dont vous avez besoin pour vos types d’enregistrements.You can add any other members you need for your record types. Vous pouvez créer des types d’enregistrements immuables sachant qu’aucun des membres générés par le compilateur ne fera passer l’État.You can create immutable record types knowing that none of the compiler-generated members would mutate state. Pour les enregistrements positionnels, les expressions facilitent la with prise en charge de la mutation non destructrice.For positional records, with expressions make it easy to support non-destructive mutation.

Les enregistrements ajoutent une autre façon de définir des types.Records add another way to define types. classLes définitions vous permettent de créer des hiérarchies orientées objet qui se concentrent sur les responsabilités et le comportement des objets.You use class definitions to create object-oriented hierarchies that focus on the responsibilities and behavior of objects. Vous créez struct des types pour les structures de données qui stockent les données et sont suffisamment petits pour être copiés efficacement.You create struct types for data structures that store data and are small enough to copy efficiently. Vous créez des enregistrements lorsque vous souhaitez une comparaison et une égalité basée sur les valeurs, que vous ne voulez pas copier de valeurs et que vous souhaitez utiliser des variables de référence.You create records when you want value-based equality and comparison, don't want to copy values, and want to use reference variables.

Vous pouvez apprendre la description complète des enregistrements en lisant la spécification de type d’enregistrement proposé.You can learn the complete description of records by reading the proposed record type specification.