Nouveautés dans C# 9.0
C# 9.0 ajoute les fonctionnalités et améliorations suivantes au langage C# :
- Enregistrements
- Setter init uniquement
- Instructions de niveau supérieur
- Améliorations des critères spéciaux
- Performances et interopérabilité
- Caractéristiques d’ajustement et de finition
- Prise en charge des générateurs de code
- Vague d’avertissement 5
C# 9.0 est pris en charge par .NET 5. Pour plus d’informations, consultez contrôle de version du langage C#.
Vous pouvez télécharger le dernier SDK .NET à partir de la page de téléchargements .NET.
Types d’enregistrements
C# 9.0 introduit des types d’enregistrements. Utilisez le mot clé record
pour définir un type de référence qui fournit des fonctionnalités intégrées pour l’encapsulation de données. Vous pouvez créer des types d’enregistrements avec des propriétés immuables à l’aide de paramètres positionnels ou de la syntaxe de propriété standard :
public record Person(string FirstName, string LastName);
public record Person
{
public string FirstName { get; init; } = default!;
public string LastName { get; init; } = default!;
};
Vous pouvez également créer des types d’enregistrements avec des propriétés et des champs mutables :
public record Person
{
public string FirstName { get; set; } = default!;
public string LastName { get; set; } = default!;
};
Bien que les enregistrements puissent être mutables, ils sont principalement destinés à prendre en charge des modèles de données immuables. Le type d’enregistrement offre les fonctionnalités suivantes :
- Syntaxe concise pour la création d’un type de référence avec des propriétés immuables
- Comportement utile pour un type de référence centré sur les données :
- Prise en charge des hiérarchies d’héritage
Vous pouvez utiliser des types de structure pour concevoir des types centrés sur les données qui fournissent une égalité de valeur avec peu ou pas de comportement. Toutefois, pour les modèles de données relativement volumineux, les types de structure ont certains inconvénients :
- Ils ne prennent pas en charge l’héritage.
- Ils sont moins efficaces pour déterminer l’égalité des valeurs. Pour les types valeur, la méthode ValueType.Equals utilise la réflexion pour rechercher tous les champs. Pour les enregistrements, le compilateur génère la méthode
Equals
. En pratique, l’implémentation de l’égalité des valeurs dans les enregistrements est beaucoup plus rapide. - Ils utilisent plus de mémoire dans certains scénarios, car chaque instance a une copie complète de toutes les données. Les types d’enregistrements sont des types de référence, donc une instance d’enregistrement ne contient qu’une référence aux données.
Syntaxe positionnelle pour la définition de propriété
Vous pouvez utiliser des paramètres positionnels pour déclarer des propriétés d’un enregistrement et initialiser les valeurs de propriété lorsque vous créez une instance :
public record Person(string FirstName, string LastName);
public static void Main()
{
Person person = new("Nancy", "Davolio");
Console.WriteLine(person);
// output: Person { FirstName = Nancy, LastName = Davolio }
}
Lorsque vous utilisez la syntaxe positionnelle pour la définition de propriété, le compilateur crée :
- Une propriété publique implémentée automatiquement init-only pour chaque paramètre positionnel fourni dans la déclaration d’enregistrement. Une propriété init-only peut être définie dans le constructeur ou à l’aide d’un initialiseur de propriété.
- Un constructeur principal dont les paramètres correspondent aux paramètres positionnels de la déclaration d’enregistrement.
- Une méthode
Deconstruct
avec un paramètreout
pour chaque paramètre positionnel fourni dans la déclaration d’enregistrement.
Pour plus d’informations, consultez Syntaxe positionnelle dans l’article de référence du langage C# sur les enregistrements.
Immuabilité
Un type d’enregistrement n’est pas nécessairement immuable. Vous pouvez déclarer des propriétés avec des accesseurs set
et des champs autres que readonly
. Toutefois, bien que les enregistrements puissent être mutables, ils facilitent la création de modèles de données immuables. Les propriétés que vous créez à l’aide de la syntaxe positionnelle sont immuables.
L’immuabilité peut être utile lorsque vous souhaitez qu’un type centré sur les données soit thread-safe ou qu’un code de hachage reste le même dans une table de hachage. Cela peut empêcher les bogues qui se produisent lorsque vous transmettez un argument par référence à une méthode, et la méthode modifie de façon inattendue la valeur de l’argument.
Les fonctionnalités propres aux types d’enregistrements sont implémentées par des méthodes synthétisées par le compilateur, et aucune de ces méthodes ne compromet l’immuabilité en modifiant l’état de l’objet.
Égalité des valeurs
L’égalité des valeurs signifie que deux variables d’un type d’enregistrement sont égales si les types correspondent et que toutes les valeurs de propriété et de champ correspondent. Pour d’autres types de référence, l’égalité signifie l’identité. Autrement dit, deux variables d’un type référence sont égales si elles font référence au même objet.
L’exemple suivant illustre l’égalité des valeurs des types d’enregistrements :
public record Person(string FirstName, string LastName, string[] PhoneNumbers);
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
}
Dans les types class
, vous pouvez remplacer manuellement les méthodes d’égalité et les opérateurs pour atteindre l’égalité des valeurs, mais le développement et le test de ce code seraient fastidieux et sujets aux erreurs. Le fait d’avoir cette fonctionnalité intégrée empêche les bogues qui résulteraient de l’oubli de mettre à jour le code de remplacement personnalisé lorsque des propriétés ou des champs sont ajoutés ou modifiés.
Pour plus d’informations, consultez Égalité des valeurs dans l’article de référence du langage C# sur les enregistrements.
Mutation non destructrice
Si vous voulez muter les propriétés immuables d’une instance d’enregistrement, vous pouvez utiliser une expression with
pour obtenir une mutation non destructrice. Une expression with
crée une nouvelle instance d’enregistrement qui est une copie d’une instance d’enregistrement existante, avec des propriétés et des champs spécifiés modifiés. Utilisez la syntaxe d’initialiseur d’objet pour spécifier les valeurs à modifier, comme illustré dans l’exemple suivant :
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; }
}
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
}
Pour plus d’informations, consultez Mutation non destructrice dans l’article de référence du langage C# sur les enregistrements.
Mise en forme intégrée pour l’affichage
Les types d’enregistrements ont une méthode ToString générée par le compilateur qui affiche les noms et les valeurs des propriétés publiques et des champs. La méthode ToString
retourne une chaîne au format suivant :
<nom du type d’enregistrement> { <nom de propriété> = <valeur>, <nom de propriété> = <valeur>, ...}
Pour les types de référence, le nom de type de l’objet auquel la propriété fait référence est affiché au lieu de la valeur de propriété. Dans l’exemple suivant, le tableau est un type de référence. System.String[]
est donc affiché au lieu des valeurs d’élément de tableau réelles :
Person { FirstName = Nancy, LastName = Davolio, ChildNames = System.String[] }
Pour plus d’informations, consultez Mise en forme intégrée dans l’article de référence du langage C# sur les enregistrements.
Héritage
Un enregistrement peut hériter d’un autre enregistrement. Toutefois, un enregistrement ne peut pas hériter d’une classe et une classe ne peut pas hériter d’un enregistrement.
L’exemple suivant illustre l’héritage avec la syntaxe de propriété positionnelle :
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}
Pour que deux variables d’enregistrement soient égales, le type d’exécution doit être égal. Les types des variables conteneur peuvent être différents. Ceci est illustré dans l'exemple de code suivant :
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Person student = new Student("Nancy", "Davolio", 3);
Console.WriteLine(teacher == student); // output: False
Student student2 = new Student("Nancy", "Davolio", 3);
Console.WriteLine(student2 == student); // output: True
}
Dans l’exemple, toutes les instances ont les mêmes propriétés et les mêmes valeurs de propriété. Mais student == teacher
retourne False
bien que les deux soient des variables de type Person
. Et student == student2
retourne True
bien qu’il s’agit d’une variable Person
et d’une variable Student
.
Toutes les propriétés publiques et les champs des types dérivés et de base sont inclus dans la sortie ToString
, comme illustré dans l’exemple suivant :
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
Console.WriteLine(teacher);
// output: Teacher { FirstName = Nancy, LastName = Davolio, Grade = 3 }
}
Pour plus d’informations, consultez Héritage dans l’article de référence sur le langage C# sur les enregistrements.
Setter init uniquement
Les setters init-only fournissent une syntaxe cohérente pour initialiser les membres d’un objet. Les initialiseurs de propriété indiquent clairement quelle valeur définit la propriété. L’inconvénient est que ces propriétés doivent être définies. À compter de C# 9.0, vous pouvez créer des accesseurs init
au lieu d’accesseurs set
pour les propriétés et les indexeurs. Les appelants peuvent utiliser la syntaxe d’initialiseur de propriété pour définir ces valeurs dans les expressions de création, mais ces propriétés sont en lecture seule une fois la construction terminée. Les setters init-only fournissent uniquement une fenêtre pour modifier l’état. Cette fenêtre se ferme lorsque la phase de construction est terminée. La phase de construction se termine après toutes les initialisations, y compris celles des initialiseurs de propriétés et des expressions « with ».
Vous pouvez déclarer des setters init
-only dans n’importe quel type que vous écrivez. Par exemple, la structure suivante définit une structure d’observation météorologique :
public struct WeatherObservation
{
public DateTime RecordedAt { get; init; }
public decimal TemperatureInCelsius { get; init; }
public decimal PressureInMillibars { get; init; }
public override string ToString() =>
$"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
$"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}
Les appelants peuvent utiliser la syntaxe d’initialiseur de propriété pour définir les valeurs tout en conservant l’immuabilité :
var now = new WeatherObservation
{
RecordedAt = DateTime.Now,
TemperatureInCelsius = 20,
PressureInMillibars = 998.0m
};
Une tentative de modification d’une observation après l’initialisation entraîne une erreur du compilateur :
// Error! CS8852.
now.TemperatureInCelsius = 18;
Les setters init-only peuvent être utiles pour définir les propriétés de classe de base à partir de classes dérivées. Ils peuvent également définir des propriétés dérivées par le biais d’assistances dans une classe de base. Les enregistrements positionnels déclarent des propriétés à l’aide de setters init-only. Ces setters sont utilisés dans des expressions. Vous pouvez déclarer des setters init-only pour chaque class
, struct
ou record
que vous définissez.
Pour plus d’informations, consultez init (Référence C#).
Instructions de niveau supérieur
Les instructions de niveau supérieur suppriment les procédés inutile de nombreuses applications. Considérez le programme canonique « Hello World! » :
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Il n’y a qu’une seule ligne de code active. Avec les instructions de niveau supérieur, vous pouvez remplacer toutes ces instructions réutilisables par la directive using
et la seule ligne active :
using System;
Console.WriteLine("Hello World!");
Si vous vouliez un programme à ligne unique, vous pouvez supprimer la directive using
et utiliser le nom de type complet :
System.Console.WriteLine("Hello World!");
Un seul fichier de votre application peut utiliser des instructions de niveau supérieur. Si le compilateur trouve des instructions de niveau supérieur dans plusieurs fichiers sources, il s’agit d’une erreur. Il s’agit également d’une erreur si vous combinez des instructions de niveau supérieur avec une méthode de point d’entrée de programme déclarée, généralement une méthode Main
. Dans un sens, vous pouvez penser qu’un fichier contient les instructions qui seraient normalement dans la méthode Main
d’une classe Program
.
L’une des utilisations les plus courantes de cette fonctionnalité consiste à créer des supports d’enseignement. Les développeurs C# débutants peuvent écrire le « Hello World! » canonique dans une ou deux lignes de code. Aucun procédé supplémentaire n’est nécessaire. Toutefois, les développeurs expérimentés trouveront de nombreuses utilisations pour cette fonctionnalité. Les instructions de niveau supérieur permettent une expérience de type script pour l’expérimentation similaire à ce que fournissent les notebooks Jupyter. Les instructions de niveau supérieur sont idéales pour les petits programmes et utilitaires de console. Azure Functions est un cas d’usage idéal pour les instructions de niveau supérieur.
Plus important encore, les instructions de niveau supérieur ne limitent pas l’étendue ou la complexité de votre application. Ces instructions peuvent accéder ou utiliser n’importe quelle classe .NET. Elles ne limitent pas non plus votre utilisation d’arguments de ligne de commande ou de valeurs de retour. Les instructions de niveau supérieur peuvent accéder à un tableau de chaînes nommé args
. Si les instructions de niveau supérieur retournent une valeur entière, cette valeur devient le code de retour entier d’une méthode Main
synthétisée. Les instructions de niveau supérieur peuvent contenir des expressions asynchrones. Dans ce cas, le point d’entrée synthétisé retourne Task
, ou Task<int>
.
Pour plus d’informations, consultez Instructions de niveau supérieur dans le Guide de programmation C#.
Améliorations des critères spéciaux
C# 9 inclut de nouvelles améliorations de correspondance de modèle :
- Les modèles de type correspondent à un objet qui correspond à un type particulier
- Les modèles entre parenthèses appliquent ou mettent en évidence la priorité des combinaisons de modèles
- Les modèles conjonctifs
and
nécessitent que les deux modèles correspondent - Les modèles disjonctifs
or
nécessitent une correspondance entre les deux modèles - Les modèles non validés
not
nécessitent qu’un modèle ne corresponde pas - Les modèles relationnels nécessitent que l’entrée soit inférieure, supérieure ou égale à une constante donnée ou supérieure ou égale à une constante donnée.
Ces modèles enrichissent la syntaxe des modèles. Prenons les exemples suivants :
public static bool IsLetter(this char c) =>
c is >= 'a' and <= 'z' or >= 'A' and <= 'Z';
L’ajout de parenthèses facultatives indique clairement que and
a une priorité plus élevée que or
:
public static bool IsLetterOrSeparator(this char c) =>
c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';
L’une des utilisations les plus courantes est une nouvelle syntaxe pour une vérification null :
if (e is not null)
{
// ...
}
L’un de ces modèles peut être utilisé dans n’importe quel contexte où les modèles sont autorisés : expressions de modèle is
, expressions switch
, modèles imbriqués et modèle d’étiquette case
d’une instruction switch
.
Pour plus d’informations, consultez Modèles (référence C#).
Pour plus d’informations, consultez les sections Modèles relationnels et Modèles logiques de l’article Modèles.
Performances et interopérabilité
Trois nouvelles fonctionnalités améliorent la prise en charge de l’interopérabilité native et des bibliothèques de bas niveau qui nécessitent une haute performance : les entiers de taille native, les pointeurs de fonction et l’omission d’indicateur localsinit
.
Les entiers de taille native, nint
et nuint
, sont des types entiers. Ils sont exprimés par les types sous-jacents System.IntPtr et System.UIntPtr. Le compilateur affiche des conversions et des opérations supplémentaires pour ces types en tant qu’entiers natifs. Les entiers de taille native définissent des propriétés pour MaxValue
ou MinValue
. Ces valeurs ne peuvent pas être exprimées en tant que constantes au moment de la compilation, car elles dépendent de la taille native d’un entier sur l’ordinateur cible. Ces valeurs sont lues en lecture seule au moment de l’exécution. Vous pouvez utiliser des valeurs constantes pour nint
sur la plage [int.MinValue
.. int.MaxValue
]. Vous pouvez utiliser des valeurs constantes pour nuint
sur la plage [uint.MinValue
.. uint.MaxValue
]. Le compilateur effectue un pliage constant pour tous les opérateurs unaires et binaires à l’aide des types System.Int32 et System.UInt32. Si le résultat ne correspond pas à 32 bits, l’opération est exécutée au moment du runtime et n’est pas considérée comme une constante. Les entiers de taille native peuvent augmenter les performances dans les scénarios où les mathématiques entières sont utilisées en grande partie et doivent avoir les performances les plus rapides possibles. Pour plus d’informations, consultez nint
et types nuint
.
Les pointeurs de fonction fournissent une syntaxe simple pour accéder aux opcodes IL ldftn
et calli
. Vous pouvez déclarer des pointeurs de fonction à l’aide de la nouvelle syntaxe delegate*
. Un type delegate*
est un type de pointeur. L’appel du type delegate*
utilise calli
, contrairement à un délégué qui utilise callvirt
sur la méthode Invoke()
. Sur le plan syntaxique, les appels sont identiques. L’appel de pointeur de fonction utilise la convention d’appel managed
. Ajoutez le mot clé unmanaged
après la syntaxe delegate*
pour déclarer que vous souhaitez la convention d’appel unmanaged
. D’autres conventions d’appel peuvent être spécifiées à l’aide d’attributs sur la déclaration delegate*
. Pour plus d’informations, consultez Types de code et de pointeur non sécurisés.
Enfin, vous pouvez ajouter System.Runtime.CompilerServices.SkipLocalsInitAttribute pour indiquer au compilateur de ne pas émettre l’indicateur localsinit
. Cet indicateur indique au CLR d’effectuer l’initialisation zéro de toutes les variables locales. L’indicateur localsinit
est le comportement par défaut pour C# depuis la version 1.0. Toutefois, l’initialisation zéro peut avoir un impact mesurable sur les performances dans certains scénarios. Surtout lorsque vous utilisez stackalloc
. Dans ces cas, vous pouvez ajouter SkipLocalsInitAttribute. Vous pouvez l’ajouter à une seule méthode ou propriété, ou à class
, struct
interface
ou même à un module. Cet attribut n’affecte pas les méthodes abstract
; elle affecte le code généré pour l’implémentation. Pour plus d'informations, consultez Attribut SkipLocalsInit
.
Ces fonctionnalités peuvent améliorer les performances dans certains scénarios. Elles ne doivent être utilisées qu’après une évaluation minutieuse avant et après l’adoption. Le code impliquant des entiers de taille native doit être testé sur plusieurs plateformes cibles avec des tailles entières différentes. Les autres fonctionnalités nécessitent du code non sécurisé.
Caractéristiques d’ajustement et de finition
De nombreuses autres fonctionnalités peuvent vous aider à écrire du code plus efficacement. Dans C# 9.0, vous pouvez omettre le type dans une expression new
lorsque le type de l’objet créé est déjà connu. L’utilisation la plus courante est dans les déclarations de champ :
private List<WeatherObservation> _observations = new();
Le type cible new
peut également être utilisé lorsque vous devez créer un objet à passer en tant qu’argument à une méthode. Imaginez une méthode ForecastFor()
avec la signature suivante :
public WeatherForecast ForecastFor(DateTime forecastDate, WeatherForecastOptions options)
Vous pouvez l’appeler comme suit :
var forecast = station.ForecastFor(DateTime.Now.AddDays(2), new());
Une autre utilisation intéressante pour cette fonctionnalité consiste à la combiner avec des propriétés init-only pour initialiser un nouvel objet :
WeatherStation station = new() { Location = "Seattle, WA" };
Vous pouvez retourner une instance créée par le constructeur par défaut à l’aide d’une instruction return new();
.
Une fonctionnalité similaire améliore la résolution de type cible des expressions conditionnelles. Avec cette modification, les deux expressions n’ont pas besoin d’une conversion implicite de l’une vers l’autre, mais peuvent avoir des conversions implicites vers un type cible. Vous ne remarquerez probablement pas ce changement. Vous remarquerez cependant que désormais, certaines expressions conditionnelles qui nécessitaient des casts ou qui ne se compilaient pas fonctionnent.
À partir de C# 9.0, vous pouvez ajouter le modificateur static
à des expressions lambda ou à des méthodes anonymes. Les expressions lambda statiques sont analogues aux fonctions locales static
: une méthode lambda statique ou anonyme ne peut pas capturer les variables locales ou l’état de l’instance. Le modificateur static
empêche la capture accidentelle d’autres variables.
Les types de retour covariant offrent une flexibilité pour les types de retour de méthodes de remplacement. Une méthode de remplacement peut retourner un type dérivé depuis un type de retour de la méthode de base substituée. Cela peut être utile pour les enregistrements et pour d’autres types qui prennent en charge les méthodes de clone virtuel ou de fabrique.
En outre, la boucle foreach
reconnaît et utilise une méthode d’extension GetEnumerator
alternative pour le modèle foreach
. Cette modification signifie que foreach
est cohérent avec d’autres constructions basées sur des modèles, telles que le modèle asynchrone et la déconstruction basée sur des modèles. En pratique, cette modification signifie que vous pouvez ajouter la prise en charge foreach
à n’importe quel type. Vous devez limiter son utilisation lors de l’énumération d’un objet dans votre conception.
Vous pouvez ensuite utiliser des abandons en tant que paramètres pour les expressions lambda. Cette commodité vous permet d’éviter de nommer l’argument, et le compilateur peut éviter de l’utiliser. Utilisez _
pour n’importe quel argument. Pour plus d’informations, consultez la section Paramètres d’entrée d’une expression lambda de l’article Expressions lambda.
Vous pouvez désormais appliquer des attributs aux fonctions locales. Par exemple, vous pouvez appliquer des annotations d’attribut nullables aux fonctions locales.
Prise en charge des générateurs de code
Deux fonctionnalités finales prennent en charge les générateurs de code C#. Les générateurs de code C# sont un composant que vous pouvez écrire. Il est similaire à un analyseur Roslyn ou à un correctif de code. La différence est que les générateurs de code analysent le code et écrivent de nouveaux fichiers de code source dans le cadre du processus de compilation. Un générateur de code classique recherche du code pour les attributs ou autres conventions.
Un générateur de code lit des attributs ou autres éléments de code à l’aide des API d’analyse Roslyn. À partir de ces informations, du nouveau code est ajouté à la compilation. Les générateurs sources peuvent uniquement ajouter du code ; ils ne sont pas autorisés à modifier du code existant dans la compilation.
Les deux fonctionnalités ajoutées pour les générateurs de code sont des extensions à la syntaxe de méthode partielle et aux initialiseurs de module. Tout d’abord, les modifications apportées aux méthodes partielles. Avant C# 9.0, les méthodes partielles sont private
mais ne peuvent pas spécifier de modificateur d’accès, avoir un retour void
et ne peuvent pas avoir de paramètres out
. Ces restrictions signifient que si aucune implémentation de méthode n’est fournie, le compilateur supprime tous les appels à la méthode partielle. C# 9.0 supprime ces restrictions, mais nécessite que les déclarations de méthode partielles aient une implémentation. Les générateurs de code peuvent fournir cette implémentation. Pour éviter d’introduire un changement cassant, le compilateur considère toute méthode partielle sans modificateur d’accès pour suivre les anciennes règles. Si la méthode partielle inclut le modificateur d’accès private
, les nouvelles règles régissent cette méthode partielle. Pour plus d'informations, consultez Méthode partielles (référence C#).
La deuxième nouvelle fonctionnalité pour les générateurs de code sont les initialiseurs de module. Les initialiseurs de module sont des méthodes qui ont l’attribut ModuleInitializerAttribute attaché à eux. Ces méthodes seront appelées par le runtime avant tout autre appel de champ ou appel de méthode dans l’ensemble du module. Méthode d’initialiseur de module :
- Doit être statique
- Doit être sans paramètre
- Doit retourner void
- Ne doit pas être une méthode générique
- Ne doit pas être contenu dans une classe générique
- Doit être accessible à partir du module conteneur
Ce dernier point de puce signifie que la méthode et sa classe conteneur doivent être internes ou publiques. La méthode ne peut pas être une fonction locale. Pour plus d'informations, consultez Attribut ModuleInitializer
.