Nouveautés de C# 7.0What's new in C# 7.0

C# 7.0 ajoute un certain nombre de nouvelles fonctionnalités au langage C# :C# 7.0 adds a number of new features to the C# language:

  • Variables outout variables
    • Vous pouvez déclarer des valeurs out inline comme arguments de la méthode dans laquelle elles sont utilisées.You can declare out values inline as arguments to the method where they're used.
  • TuplesTuples
    • Vous pouvez créer des types légers et sans nom qui contiennent plusieurs champs publics.You can create lightweight, unnamed types that contain multiple public fields. Les compilateurs et les outils de l’IDE comprennent la sémantique de ces types.Compilers and IDE tools understand the semantics of these types.
  • Éléments ignorésDiscards
    • Les éléments ignorés sont les variables temporaires en écriture seule utilisées dans les attributions quand vous ne vous souciez pas de la valeur assignée.Discards are temporary, write-only variables used in assignments when you don't care about the value assigned. Ils s’avèrent utiles lors de la déconstruction de tuples et de types définis par l’utilisateur, ainsi que lors de l’appel de méthodes avec des paramètres out.They're most useful when deconstructing tuples and user-defined types, as well as when calling methods with out parameters.
  • Critères spéciauxPattern Matching
    • Vous pouvez créer une logique de branchement basée sur des types arbitraires et les valeurs des membres de ces types.You can create branching logic based on arbitrary types and values of the members of those types.
  • Variables locales et retours refref locals and returns
    • Les valeurs de retour et les variables locales de méthode peuvent être des références à un autre stockage.Method local variables and return values can be references to other storage.
  • Fonctions localesLocal Functions
    • Vous pouvez imbriquer des fonctions dans d’autres fonctions afin de limiter leur portée et leur visibilité.You can nest functions inside other functions to limit their scope and visibility.
  • Autres membres expression-bodiedMore expression-bodied members
    • La liste des membres pouvant être créés à l’aide d’expressions s’est allongée.The list of members that can be authored using expressions has grown.
  • Expressions throwthrow Expressions
    • Vous pouvez lever des exceptions dans les constructions de code qui n’étaient pas autorisées auparavant, car throw était une instruction.You can throw exceptions in code constructs that previously weren't allowed because throw was a statement.
  • Types de retour async généralisésGeneralized async return types
    • Les méthodes déclarées avec le modificateur async peuvent retourner d’autres types en plus de Task et de Task<T>.Methods declared with the async modifier can return other types in addition to Task and Task<T>.
  • Améliorations de la syntaxe littérale numériqueNumeric literal syntax improvements
    • De nouveaux jetons améliorent la lisibilité des constantes numériques.New tokens improve readability for numeric constants.

Le reste de cet article présente une vue d’ensemble de chaque fonctionnalité.The remainder of this article provides an overview of each feature. Vous découvrirez la logique de chacune d’elles.For each feature, you'll learn the reasoning behind it. Vous allez apprendre leur syntaxe.You'll learn the syntax. Vous pouvez explorer ces fonctionnalités dans votre environnement à l’aide de l’outil global dotnet try :You can explore these features in your environment using the dotnet try global tool:

  1. Installez l’outil global dotnet-try.Install the dotnet-try global tool.
  2. Clonez le référentiel dotnet/try-samples.Clone the dotnet/try-samples repository.
  3. Définissez le répertoire actuel sur le sous-répertoire csharp7 pour le référentiel try-samples.Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. Exécutez dotnet try.Run dotnet try.

Variables outout variables

La syntaxe existante qui prend en charge les paramètres out a été améliorée dans cette version.The existing syntax that supports out parameters has been improved in this version. Vous pouvez désormais déclarer des variables out dans la liste d’arguments d’un appel de méthode, au lieu d’écrire une instruction de déclaration distincte :You can now declare out variables in the argument list of a method call, rather than writing a separate declaration statement:

if (int.TryParse(input, out int result))
    Console.WriteLine(result);
else
    Console.WriteLine("Could not parse input");

Par souci de clarté, vous voulez peut-être spécifier le type de la variable out, comme indiqué ci-dessus.You may want to specify the type of the out variable for clarity, as shown above. Toutefois, le langage prend en charge l’utilisation d’une variable locale implicitement typée :However, the language does support using an implicitly typed local variable:

if (int.TryParse(input, out var answer))
    Console.WriteLine(answer);
else
    Console.WriteLine("Could not parse input");
  • Le code est plus facile à lire.The code is easier to read.
    • Vous déclarez la variable out à l’endroit où vous l’utilisez, et non pas sur une autre ligne au-dessus.You declare the out variable where you use it, not on another line above.
  • Il n’est pas nécessaire d’assigner une valeur initiale.No need to assign an initial value.
    • En déclarant la variable out à l’endroit où elle est utilisée dans un appel de méthode, vous ne pouvez pas l’utiliser accidentellement avant qu’elle soit assignée.By declaring the out variable where it's used in a method call, you can't accidentally use it before it is assigned.

TuplesTuples

C# fournit une syntaxe complète pour les classes et les structs, utilisée pour expliquer l’intention de votre conception.C# provides a rich syntax for classes and structs that is used to explain your design intent. Cependant, cette syntaxe complète nécessite parfois un travail supplémentaire avec peu d’avantages.But sometimes that rich syntax requires extra work with minimal benefit. Vous pouvez souvent écrire des méthodes qui nécessitent une structure simple contenant plusieurs éléments de données.You may often write methods that need a simple structure containing more than one data element. Pour prendre en charge ces scénarios, des tuples ont été ajoutées à C#.To support these scenarios tuples were added to C#. Les tuples sont des structures de données légères contenant plusieurs champs pour représenter les membres de données.Tuples are lightweight data structures that contain multiple fields to represent the data members. Les champs ne sont pas validés et vous ne pouvez pas définir vos propres méthodes.The fields aren't validated, and you can't define your own methods

Notes

Les tuples étaient disponibles avant C# 7.0, mais ils n’étaient pas efficaces et n’avaient aucune prise en charge du langage.Tuples were available before C# 7.0, but they were inefficient and had no language support. Cela signifiait que les éléments tuples pouvaient uniquement être référencés comme Item1, Item2, et ainsi de suite.This meant that tuple elements could only be referenced as Item1, Item2 and so on. C# 7.0 introduit la prise en charge du langage pour les tuples, ce qui permet d’utiliser des noms sémantiques pour les champs d’un tuple avec de nouveaux types tuple plus efficaces.C# 7.0 introduces language support for tuples, which enables semantic names for the fields of a tuple using new, more efficient tuple types.

Vous pouvez créer un tuple en assignant une valeur à chaque membre et éventuellement, en fournissant des noms sémantiques à chacun des membres du tuple :You can create a tuple by assigning a value to each member, and optionally providing semantic names to each of the members of the tuple:

(string Alpha, string Beta) namedLetters = ("a", "b");
Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");

Le tuple namedLetters contient des champs appelés Alpha et Beta.The namedLetters tuple contains fields referred to as Alpha and Beta. Ces noms n’existent qu’au moment de la compilation et ne sont pas conservés par exemple lors de l’inspection du tuple à l’aide de la réflexion lors de l’exécution.Those names exist only at compile time and aren't preserved, for example when inspecting the tuple using reflection at runtime.

Dans une assignation de tuple, vous pouvez également spécifier les noms des champs dans la partie droite :In a tuple assignment, you can also specify the names of the fields on the right-hand side of the assignment:

var alphabetStart = (Alpha: "a", Beta: "b");
Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");

Dans certains cas, vous pouvez souhaiter décompresser les membres d’un tuple qui ont été retournés à partir d’une méthode.There may be times when you want to unpackage the members of a tuple that were returned from a method. Pour ce faire, déclarez des variables distinctes pour chacune des valeurs dans le tuple.You can do that by declaring separate variables for each of the values in the tuple. Cette décompression est appelée déconstruction du tuple :This unpackaging is called deconstructing the tuple:

(int max, int min) = Range(numbers);
Console.WriteLine(max);
Console.WriteLine(min);

Vous pouvez également fournir une déconstruction similaire pour tout type dans le .NET.You can also provide a similar deconstruction for any type in .NET. Vous écrivez une méthode Deconstruct en tant que membre de la classe.You write a Deconstruct method as a member of the class. Cette méthode Deconstruct fournit un ensemble d’arguments out pour chacune des propriétés que vous voulez extraire.That Deconstruct method provides a set of out arguments for each of the properties you want to extract. Prenons la classe Point suivante qui fournit une méthode de déconstructeur qui extrait les coordonnées X et Y :Consider this Point class that provides a deconstructor method that extracts the X and Y coordinates:

   public class Point
   {
       public Point(double x, double y) 
           => (X, Y) = (x, y);

       public double X { get; }
       public double Y { get; }

       public void Deconstruct(out double x, out double y) =>
           (x, y) = (X, Y);
   }

Vous pouvez extraire les champs individuels en affectant un Point à un tuple :You can extract the individual fields by assigning a Point to a tuple:

var p = new Point(3.14, 2.71);
(double X, double Y) = p;

Vous pouvez approfondir vos connaissances sur les tuples avec cet article.You can learn more in depth about tuples in the tuples article.

Éléments ignorésDiscards

Souvent, lors de la déconstruction d’un tuple ou de l’appel d’une méthode avec des paramètres out, vous devez définir une variable dont la valeur ne vous importe pas et que vous ne prévoyez pas d’utiliser.Often when deconstructing a tuple or calling a method with out parameters, you're forced to define a variable whose value you don't care about and don't intend to use. C# ajoute la prise en charge des éléments ignorés pour gérer ce scénario.C# adds support for discards to handle this scenario. Un élément ignoré est une variable en écriture seule dont le nom est _ (caractère de soulignement) ; vous pouvez assigner toutes les valeurs que vous souhaitez ignorer à la variable unique.A discard is a write-only variable whose name is _ (the underscore character); you can assign all of the values that you intend to discard to the single variable. Un élément ignoré est semblable à une variable non assignée ; en dehors de l’instruction d’assignation, l’élément ignoré ne peut pas être utilisé dans le code.A discard is like an unassigned variable; apart from the assignment statement, the discard can't be used in code.

Les éléments ignorés sont pris en charge dans les scénarios suivants :Discards are supported in the following scenarios:

  • Lors de la déconstruction de tuples ou de types définis par l’utilisateur.When deconstructing tuples or user-defined types.
  • Lors d’appels à des méthodes avec des paramètres out.When calling methods with out parameters.
  • Dans une opération de critères spéciaux avec les instructions is et switch.In a pattern matching operation with the is and switch statements.
  • Comme un identificateur autonome quand vous voulez explicitement identifier la valeur d’une assignation comme un élément ignoré.As a standalone identifier when you want to explicitly identify the value of an assignment as a discard.

L’exemple suivant définit une méthode QueryCityDataForYears qui retourne un tuple à 6 composants qui contient des données pour une ville au cours de deux années différentes.The following example defines a QueryCityDataForYears method that returns a 6-tuple that contains data for a city for two different years. L’appel de méthode dans l’exemple s’intéresse uniquement à deux valeurs de population retournées par la méthode et traite par conséquent les valeurs restantes dans le tuple comme des éléments ignorés lors de la déconstruction du tuple.The method call in the example is concerned only with the two population values returned by the method and so treats the remaining values in the tuple as discards when it deconstructs the tuple.

using System;
using System.Collections.Generic;

public class Example
{
    public static void Main()
    {
        var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

        Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
    }
   
    private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
    {
        int population1 = 0, population2 = 0;
        double area = 0;
      
        if (name == "New York City")
        {
            area = 468.48; 
            if (year1 == 1960)
            {
                population1 = 7781984;
            }
            if (year2 == 2010)
            {
                population2 = 8175133;
            }
            return (name, area, year1, population1, year2, population2);
        }

        return ("", 0, 0, 0, 0, 0);
    }
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

Pour plus d’informations, consultez Éléments ignorés.For more information, see Discards.

Critères spéciauxPattern matching

Les critères spéciaux constituent une fonctionnalité qui vous permet d’implémenter la distribution de méthodes sur des propriétés autres que le type d’un objet.Pattern matching is a feature that allows you to implement method dispatch on properties other than the type of an object. Vous êtes probablement déjà familiarisé avec la distribution de méthodes en fonction du type d’un objet.You're probably already familiar with method dispatch based on the type of an object. Dans la programmation orientée objet, les méthodes virtuelles et override fournissent une syntaxe du langage permettant d’implémenter la distribution de méthodes basées sur le type d’un objet.In object-oriented programming, virtual and override methods provide language syntax to implement method dispatching based on an object's type. Les classes de base et dérivées offrent des implémentations différentes.Base and Derived classes provide different implementations. Les expressions de critères spéciaux étendent ce concept de manière à vous permettre d’implémenter facilement des modèles de distribution similaires pour les types et les éléments de données qui ne sont pas liés au moyen d’une hiérarchie d’héritage.Pattern matching expressions extend this concept so that you can easily implement similar dispatch patterns for types and data elements that aren't related through an inheritance hierarchy.

Les critères spéciaux prennent en charge les expressions is et les expressions switch.Pattern matching supports is expressions and switch expressions. L’une ou l’autre permettent d’inspecter un objet et ses propriétés afin de déterminer s’il correspond au modèle recherché.Each enables inspecting an object and its properties to determine if that object satisfies the sought pattern. Vous utilisez le mot clé when pour spécifier des règles supplémentaires pour le modèle.You use the when keyword to specify additional rules to the pattern.

L’expression de modèle is étend l’opérateur is classique pour interroger un objet sur son type et assigner le résultat dans une seule instruction.The is pattern expression extends the familiar is operator to query an object about its type and assign the result in one instruction. Le code suivant vérifie si une variable est un int, et si tel est le cas, il l’ajoute à la somme actuelle :The following code checks if a variable is an int, and if so, adds it to the current sum:

if (input is int count)
    sum += count;

Le petit exemple précédent illustre les améliorations apportées à l’expression is.The preceding small example demonstrates the enhancements to the is expression. Vous pouvez tester par rapport à des types valeur, ainsi que des types référence, et vous pouvez assigner le résultat de réussite à une nouvelle variable du type correct.You can test against value types as well as reference types, and you can assign the successful result to a new variable of the correct type.

L’expression de correspondance switch a une syntaxe familière, basée sur l’instruction switch qui fait déjà partie du langage C#.The switch match expression has a familiar syntax, based on the switch statement already part of the C# language. L’instruction switch mise à jour a plusieurs nouvelles constructions :The updated switch statement has several new constructs:

  • Le type directeur d’une expression switch n’est plus limité aux types intégraux, aux types Enum, string ou à un type nullable correspondant à l’un de ces types.The governing type of a switch expression is no longer restricted to integral types, Enum types, string, or a nullable type corresponding to one of those types. Vous pouvez utiliser n’importe quel type.Any type may be used.
  • Vous pouvez tester le type de l’expression switch dans chaque étiquette case.You can test the type of the switch expression in each case label. Comme avec l’expression is, vous pouvez assigner une nouvelle variable à ce type.As with the is expression, you may assign a new variable to that type.
  • Vous pouvez ajouter une clause when pour tester encore les conditions sur cette variable.You may add a when clause to further test conditions on that variable.
  • L’ordre des étiquettes case est à présent important.The order of case labels is now important. La première branche à faire correspondre est exécutée ; les autres sont ignorées.The first branch to match is executed; others are skipped.

Le code suivant illustre ces nouvelles fonctionnalités :The following code demonstrates these new features:

public static int SumPositiveNumbers(IEnumerable<object> sequence)
{
    int sum = 0;
    foreach (var i in sequence)
    {
        switch (i)
        {
            case 0:
                break;
            case IEnumerable<int> childSequence:
            {
                foreach(var item in childSequence)
                    sum += (item > 0) ? item : 0;
                break;
            }
            case int n when n > 0:
                sum += n;
                break;
            case null:
                throw new NullReferenceException("Null found in sequence");
            default:
                throw new InvalidOperationException("Unrecognized type");
        }
    }
    return sum;
}
  • case 0: est le modèle de constante classique.case 0: is the familiar constant pattern.
  • case IEnumerable<int> childSequence: est un modèle de type.case IEnumerable<int> childSequence: is a type pattern.
  • case int n when n > 0: est un modèle de type avec une autre condition when.case int n when n > 0: is a type pattern with an additional when condition.
  • case null: est le modèle null.case null: is the null pattern.
  • default: est le cas par défaut classique.default: is the familiar default case.

Pour plus d’informations sur les critères spéciaux, consultez Critères spéciaux en C#.You can learn more about pattern matching in Pattern Matching in C#.

Variables locales et retours refRef locals and returns

Cette fonctionnalité active les algorithmes qui utilisent et retournent des références à des variables définies ailleurs.This feature enables algorithms that use and return references to variables defined elsewhere. C’est le cas, par exemple, lors de la recherche d’un emplacement unique comportant certaines caractéristiques dans une matrice de grande taille.One example is working with large matrices, and finding a single location with certain characteristics. La méthode suivante retourne une référence à ce stockage dans la matrice :The following method returns a reference to that storage in the matrix:

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Vous pouvez déclarer la valeur de retour en tant que ref et modifier cette valeur dans la matrice, comme indiqué dans le code suivant :You can declare the return value as a ref and modify that value in the matrix, as shown in the following code:

ref var item = ref MatrixSearch.Find(matrix, (val) => val == 42);
Console.WriteLine(item);
item = 24;
Console.WriteLine(matrix[4, 2]);

Le langage C# a plusieurs règles qui vous protègent contre une mauvaise utilisation des variables locales et des retours ref :The C# language has several rules that protect you from misusing the ref locals and returns:

  • Vous devez ajouter le mot clé ref à la signature de méthode et à toutes les instructions return dans une méthode.You must add the ref keyword to the method signature and to all return statements in a method.
    • Cela permet de clarifier le retour par référence tout au long de la méthode.That makes it clear the method returns by reference throughout the method.
  • Un ref return peut être assigné à une variable de la valeur ou à une variable ref.A ref return may be assigned to a value variable, or a ref variable.
    • L’appelant contrôle si la valeur de retour est copiée ou non.The caller controls whether the return value is copied or not. L’omission du modificateur ref lors de l’assignation de la valeur de retour indique que l’appelant souhaite une copie de la valeur, pas une référence au stockage.Omitting the ref modifier when assigning the return value indicates that the caller wants a copy of the value, not a reference to the storage.
  • Vous ne pouvez pas affecter une valeur de retour de méthode standard à une variable locale ref.You can't assign a standard method return value to a ref local variable.
    • Cela rejette les instructions telles que ref int i = sequence.Count();.That disallows statements like ref int i = sequence.Count();
  • Vous ne pouvez pas retourner un ref à une variable dont la durée de vie ne s’étend pas au-delà de l’exécution de la méthode.You can't return a ref to a variable whose lifetime doesn't extend beyond the execution of the method.
    • Cela signifie que vous ne pouvez pas retourner une référence à une variable locale ni une variable avec une étendue similaire.That means you can't return a reference to a local variable or a variable with a similar scope.
  • Les variables locales et les retours ref ne peuvent pas être utilisés avec les méthodes Async.ref locals and returns can't be used with async methods.
    • Le compilateur ne peut pas savoir si la variable référencée a été définie à sa valeur finale quand la méthode Async est retournée.The compiler can't know if the referenced variable has been set to its final value when the async method returns.

L’ajout de variables locales ref et de retours ref permet d’utiliser des algorithmes qui sont plus efficaces en évitant la copie de valeurs, ou d’effectuer plusieurs fois des opérations de déréférencement.The addition of ref locals and ref returns enables algorithms that are more efficient by avoiding copying values, or performing dereferencing operations multiple times.

L’ajout de ref à la valeur de retour est une modification compatible avec la source.Adding ref to the return value is a source compatible change. Le code existant est compilé, mais la valeur de retour référencée est copiée lorsqu’elle est assignée.Existing code compiles, but the ref return value is copied when assigned. Les appelants doivent mettre à jour le stockage pour la valeur de retour sur une variable locale ref afin de stocker la valeur de retour en tant que référence.Callers must update the storage for the return value to a ref local variable to store the return as a reference.

Pour plus d'informations, voir l’article ref, mot clé.For more information, see the ref keyword article.

Fonctions localesLocal functions

De nombreuses conceptions pour les classes incluent des méthodes qui sont appelées à partir d’un seul emplacement.Many designs for classes include methods that are called from only one location. Ces méthodes privées supplémentaires maintiennent chaque méthode réduite et focalisée.These additional private methods keep each method small and focused. Les fonctions locales vous permettent de déclarer des méthodes dans le contexte d’une autre méthode.Local functions enable you to declare methods inside the context of another method. Il est ainsi plus facile pour les lecteurs de la classe de voir que la méthode locale est appelée uniquement à partir du contexte dans lequel elle a été déclarée.Local functions make it easier for readers of the class to see that the local method is only called from the context in which it is declared.

Il existe deux cas d’utilisation courants pour les fonctions locales : les méthodes iterator publiques et les méthodes async publiques.There are two common use cases for local functions: public iterator methods and public async methods. Ces deux types de méthodes génèrent du code qui signale les erreurs plus tard que ce qu’attendent les programmeurs.Both types of methods generate code that reports errors later than programmers might expect. Dans les méthodes iterator, toute exception est observée uniquement lors de l’appel de code qui énumère la séquence retournée.In iterator methods, any exceptions are observed only when calling code that enumerates the returned sequence. Dans les méthodes async, toute exception est observée uniquement quand le Task retourné est attendu.In async methods, any exceptions are only observed when the returned Task is awaited. L’exemple suivant illustre la séparation entre la validation de paramètres et l’implémentation de l’itérateur à l’aide d’une fonction locale :The following example demonstrates separating parameter validation from the iterator implementation using a local function:

public static IEnumerable<char> AlphabetSubset3(char start, char end)
{
    if (start < 'a' || start > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
    if (end < 'a' || end > 'z')
        throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");

    if (end <= start)
        throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");

    return alphabetSubsetImplementation();

    IEnumerable<char> alphabetSubsetImplementation()
    {
        for (var c = start; c < end; c++)
            yield return c;
    }
}

Il est possible d’utiliser la même technique avec les méthodes async pour garantir que les exceptions résultant de la validation d’argument sont levées avant le début de la tâche asynchrone :The same technique can be employed with async methods to ensure that exceptions arising from argument validation are thrown before the asynchronous work begins:

public Task<string> PerformLongRunningWork(string address, int index, string name)
{
    if (string.IsNullOrWhiteSpace(address))
        throw new ArgumentException(message: "An address is required", paramName: nameof(address));
    if (index < 0)
        throw new ArgumentOutOfRangeException(paramName: nameof(index), message: "The index must be non-negative");
    if (string.IsNullOrWhiteSpace(name))
        throw new ArgumentException(message: "You must supply a name", paramName: nameof(name));

    return longRunningWorkImplementation();

    async Task<string> longRunningWorkImplementation()
    {
        var interimResult = await FirstWork(address);
        var secondResult = await SecondStep(index, name);
        return $"The results are {interimResult} and {secondResult}. Enjoy.";
    }
}

Notes

Certaines des conceptions prises en charge par les fonctions locales peuvent également être effectuées à l’aide d’expressions lambda.Some of the designs that are supported by local functions could also be accomplished using lambda expressions. Si cela vous intéresse, reportez-vous aux informations supplémentaires décrivant ce qui différencie ces deux processus.Those interested can read more about the differences

Autres membres expression-bodiedMore expression-bodied members

C# 6 a introduit les membres expression-bodied pour les fonctions membres, ainsi que des propriétés en lecture seule.C# 6 introduced expression-bodied members for member functions, and read-only properties. C# 7.0 développe les membres autorisés pouvant être implémentés comme expressions.C# 7.0 expands the allowed members that can be implemented as expressions. Dans C# 7.0, vous pouvez implémenter des constructeurs, des finaliseurs ainsi que des accesseurs get et set sur des propriétés et des indexeurs.In C# 7.0, you can implement constructors, finalizers, and get and set accessors on properties and indexers. Le code suivant présente des exemples de chaque élément :The following code shows examples of each:

// Expression-bodied constructor
public ExpressionMembersExample(string label) => this.Label = label;

// Expression-bodied finalizer
~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");

private string label;

// Expression-bodied get / set accessors.
public string Label
{
    get => label;
    set => this.label = value ?? "Default label";
}

Notes

Cet exemple n’a pas besoin de finaliseur, mais il est présenté pour illustrer la syntaxe.This example does not need a finalizer, but it is shown to demonstrate the syntax. Vous ne devez implémenter un finaliseur dans votre classe que si cela est nécessaire pour libérer des ressources non managées.You should not implement a finalizer in your class unless it is necessary to release unmanaged resources. Vous devez également envisager d’utiliser la classe SafeHandle au lieu de gérer directement les ressources non managées.You should also consider using the SafeHandle class instead of managing unmanaged resources directly.

Ces nouveaux emplacements pour les membres expression-bodied représentent un jalon important pour le langage C# : ces fonctionnalités ont été implémentées par les membres de la communauté travaillant sur le projet open source Roslyn.These new locations for expression-bodied members represent an important milestone for the C# language: These features were implemented by community members working on the open-source Roslyn project.

La modification d’une méthode en un membre expression-bodied est une modification compatible binaire.Changing a method to an expression bodied member is a binary compatible change.

Expressions throwThrow expressions

En C#, throw a toujours été une instruction.In C#, throw has always been a statement. Étant donné que throw est une instruction, et non pas une expression, certaines constructions C# ne pouvaient pas l’utiliser.Because throw is a statement, not an expression, there were C# constructs where you couldn't use it. Il s’agit notamment des expressions conditionnelles, des expressions de fusion null, ainsi que de certaines expressions lambda.These included conditional expressions, null coalescing expressions, and some lambda expressions. L’ajout de membres expression-bodied ajoute des emplacements supplémentaires où les expressions throw seraient utiles.The addition of expression-bodied members adds more locations where throw expressions would be useful. Pour vous permettre d’écrire n’importe laquelle de ces constructions, C# 7.0 introduit les expressions throw.So that you can write any of these constructs, C# 7.0 introduces throw expressions.

Cet ajout facilite l’écriture de code davantage basé sur des expressions.This addition makes it easier to write more expression-based code. Vous n’avez pas besoin d’instructions supplémentaires pour la vérification des erreurs.You don't need additional statements for error checking.

Types de retour async généralisésGeneralized async return types

Le retour d’un objet Task à partir de méthodes async peut introduire des goulots d’étranglement au niveau des performances dans certains chemins.Returning a Task object from async methods can introduce performance bottlenecks in certain paths. Task est un type référence. Si vous l’utilisez, vous allouez donc un objet.Task is a reference type, so using it means allocating an object. Dans les cas où une méthode déclarée avec le modificateur async retourne un résultat mis en cache, ou si elle s’exécute de manière synchrone, le coût en termes de temps induit par les allocations supplémentaires peut s’avérer significatif dans les sections de code critiques pour les performances.In cases where a method declared with the async modifier returns a cached result, or completes synchronously, the extra allocations can become a significant time cost in performance critical sections of code. Cela peut devenir coûteux si ces allocations se produisent dans des boucles serrées.It can become costly if those allocations occur in tight loops.

La nouvelle fonctionnalité du langage signifie que les types de retour des méthodes async ne se limitent pas à Task, Task<T> et void.The new language feature means that async method return types aren't limited to Task, Task<T>, and void. Le type retourné doit toujours correspondre au modèle async, ce qui signifie qu’une méthode GetAwaiter doit être accessible.The returned type must still satisfy the async pattern, meaning a GetAwaiter method must be accessible. Pour donner un exemple concret, le type ValueTask a été ajouté au .NET Framework pour utiliser cette nouvelle fonctionnalité du langage :As one concrete example, the ValueTask type has been added to the .NET framework to make use of this new language feature:

public async ValueTask<int> Func()
{
    await Task.Delay(100);
    return 5;
}

Notes

Vous devez ajouter le package NuGet System.Threading.Tasks.Extensions pour pouvoir utiliser le type ValueTask<TResult>.You need to add the NuGet package System.Threading.Tasks.Extensions in order to use the ValueTask<TResult> type.

Cette amélioration s’avère particulièrement utile pour les auteurs de bibliothèques afin d’éviter d’allouer un Task dans le code critique pour les performances.This enhancement is most useful for library authors to avoid allocating a Task in performance critical code.

Améliorations de la syntaxe littérale numériqueNumeric literal syntax improvements

La mauvaise interprétation de constantes numériques peut rendre plus difficile la compréhension du code quand il est lu pour la première fois.Misreading numeric constants can make it harder to understand code when reading it for the first time. Les masques de bits ou d’autres valeurs symboliques sont source de méprise.Bit masks or other symbolic values are prone to misunderstanding. C# 7.0 comprend deux nouvelles fonctionnalités permettant d’écrire des nombres de la manière la plus lisible pour l’utilisation prévue : les littéraux binaires et les séparateurs de chiffres.C# 7.0 includes two new features to write numbers in the most readable fashion for the intended use: binary literals, and digit separators.

Dans les cas où vous créez des masques de bits chaque fois qu’une représentation binaire d’un nombre rend le code plus lisible, écrivez ce nombre au format binaire :For those times when you're creating bit masks, or whenever a binary representation of a number makes the most readable code, write that number in binary:

public const int Sixteen =   0b0001_0000;
public const int ThirtyTwo = 0b0010_0000;
public const int SixtyFour = 0b0100_0000;
public const int OneHundredTwentyEight = 0b1000_0000;

Le 0b au début de la constante indique que le nombre est écrit sous la forme d’un nombre binaire.The 0b at the beginning of the constant indicates that the number is written as a binary number. Les nombres binaires peuvent être longs. Il est donc souvent plus facile de voir les modèles de bits en introduisant le _ comme séparateur de chiffres, comme indiqué ci-dessus dans la constante binaire.Binary numbers can get long, so it's often easier to see the bit patterns by introducing the _ as a digit separator, as shown above in the binary constant. Le séparateur de chiffres peut apparaître n’importe où dans la constante.The digit separator can appear anywhere in the constant. Pour les nombres de base 10, il arrive fréquemment qu’il soit utilisé comme séparateur des milliers :For base 10 numbers, it is common to use it as a thousands separator:

public const long BillionsAndBillions = 100_000_000_000;

Il est possible d’utiliser le séparateur de chiffres également avec les types decimal, float et double :The digit separator can be used with decimal, float, and double types as well:

public const double AvogadroConstant = 6.022_140_857_747_474e23;
public const decimal GoldenRatio = 1.618_033_988_749_894_848_204_586_834_365_638_117_720_309_179M;

Globalement, vous pouvez déclarer des constantes numériques avec beaucoup plus de lisibilité.Taken together, you can declare numeric constants with much more readability.