Nouveautés de C# 6What's New in C# 6

La version Release 6.0 de C# comprenait de nombreuses fonctionnalités qui améliorent la productivité des développeurs.The 6.0 release of C# contained many features that improve productivity for developers. Cette version inclut les fonctionnalités suivantes :Features in this release include:

Globalement, ces fonctionnalités vous permettent d’écrire du code plus concis et également plus lisible.The overall effect of these features is that you write more concise code that is also more readable. La syntaxe est moins formelle concernant de nombreuses pratiques courantes.The syntax contains less ceremony for many common practices. Moins de formalisme permet de voir plus facilement l’intention de conception.It's easier to see the design intent with less ceremony. En maîtrisant ces fonctionnalités, vous serez plus productif, vous écrirez du code plus lisible et vous vous concentrerez davantage sur vos fonctionnalités principales que sur les constructions du langage.Learn these features well, and you'll be more productive, write more readable code, and concentrate more on your core features than on the constructs of the language.

Le reste de cette rubrique fournit des détails sur chacune de ces fonctionnalités.The remainder of this topic provides details on each of these features.

Améliorations des auto-propertiesAuto-Property enhancements

La syntaxe des propriétés implémentées automatiquement (généralement appelées "auto-properties") permettait de créer très facilement des propriétés qui avaient des accesseurs get et set simples :The syntax for automatically implemented properties (usually referred to as 'auto-properties') made it very easy to create properties that had simple get and set accessors:

public string FirstName { get; set; }
public string LastName { get; set; }

Toutefois, cette syntaxe simple limitait les types de conceptions que vous pouviez prendre en charge à l’aide d’auto-properties.However, this simple syntax limited the kinds of designs you could support using auto-properties. C# 6 améliore les fonctionnalités des auto-properties afin de vous permettre de les exécuter dans un plus grand nombre de scénarios.C# 6 improves the auto-properties capabilities so that you can use them in more scenarios. Vous n’aurez pas besoin de fréquemment revenir manuellement sur la syntaxe plus détaillée de la déclaration et de la manipulation du champ de stockage.You won't need to fall back on the more verbose syntax of declaring and manipulating the backing field by hand so often.

La nouvelle syntaxe répond aux scénarios de propriétés en lecture seule et d’initialisation du stockage des variables derrière une auto-property.The new syntax addresses scenarios for read-only properties, and for initializing the variable storage behind an auto-property.

Auto-properties en lecture seuleRead-only auto-properties

Les auto-properties en lecture seule offrent une syntaxe plus concise pour créer des types immuables.Read-only auto-properties provide a more concise syntax to create immutable types. Dans les versions antérieures de C#, déclarer les méthodes Set privées permettait de vous rapprocher le plus de types immuables :The closest you could get to immutable types in earlier versions of C# was to declare private setters:

public string FirstName { get; private set; }
public string LastName { get; private set; }

En utilisant cette syntaxe, le compilateur ne garantit pas que le type soit vraiment immuable.Using this syntax, the compiler doesn't ensure that the type really is immutable. Il garantit seulement que les propriétés FirstName et LastName ne sont pas modifiées à partir de code en dehors de la classe.It only enforces that the FirstName and LastName properties are not modified from any code outside the class.

Les auto-properties en lecture seule permettent un véritable comportement en lecture seule.Read-only auto-properties enable true read-only behavior. Vous déclarez l’auto-property avec uniquement un accesseur get :You declare the auto-property with only a get accessor:

public string FirstName { get; }
public string LastName { get;  }

Les propriétés FirstName et LastName peuvent être définies uniquement dans le corps d’un constructeur :The FirstName and LastName properties can be set only in the body of a constructor:

public Student(string firstName, string lastName)
{
    if (IsNullOrWhiteSpace(lastName))
        throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
    FirstName = firstName;
    LastName = lastName;
}

Tenter de définir LastName dans une autre méthode génère une erreur de compilation CS0200 :Trying to set LastName in another method generates a CS0200 compilation error:

public class Student
{
    public string LastName { get;  }

    public void ChangeName(string newLastName)
    {
        // Generates CS0200: Property or indexer cannot be assigned to -- it is read only
        LastName = newLastName;
    }
}

Cette fonctionnalité permet une véritable prise en charge du langage pour la création de types immuables et l’utilisation de la syntaxe d’auto-property plus concise et plus pratique.This feature enables true language support for creating immutable types and using the more concise and convenient auto-property syntax.

Initialiseurs d’auto-propertiesAuto-Property Initializers

Les initialiseurs d’auto-properties vous permettent de déclarer la valeur initiale d’une auto-property dans le cadre de la déclaration de la propriété.Auto-Property Initializers let you declare the initial value for an auto-property as part of the property declaration. Dans les versions antérieures, ces propriétés devaient avoir des méthodes setter que vous deviez utiliser pour initialiser le stockage de données utilisé par le champ de stockage.In earlier versions, these properties would need to have setters and you would need to use that setter to initialize the data storage used by the backing field. Étudions la classe suivante pour un étudiant. Elle contient le nom et la liste des diplômes de ce dernier :Consider this class for a student that contains the name and a list of the student's grades:

public Student(string firstName, string lastName)
{
    FirstName = firstName;
    LastName = lastName;
}

À mesure que cette classe augmente, vous pouvez inclure d’autres constructeurs.As this class grows, you may include other constructors. Chaque constructeur doit initialiser ce champ, sans quoi vous introduirez des erreurs.Each constructor needs to initialize this field, or you'll introduce errors.

C# 6 vous permet d’assigner une valeur initiale pour le stockage utilisé par une auto-property dans la déclaration de l’auto-property :C# 6 enables you to assign an initial value for the storage used by an auto-property in the auto-property declaration:

public ICollection<double> Grades { get; } = new List<double>();

Le membre Grades est initialisé à l’emplacement où il est déclaré.The Grades member is initialized where it is declared. Il est ainsi plus facile d’effectuer l’initialisation une seule fois.That makes it easier to perform the initialization exactly once. L’initialisation fait partie de la déclaration de propriété, facilitant ainsi la mise en correspondance de l’allocation de stockage et de l’interface publique pour les objets Student.The initialization is part of the property declaration, making it easier to equate the storage allocation with public interface for Student objects.

Les initialiseurs de propriété peuvent être utilisés avec des propriétés en lecture/écriture, ainsi qu’avec des propriétés en lecture seule, comme indiqué ici.Property Initializers can be used with read/write properties as well as read-only properties, as shown here.

public Standing YearInSchool { get; set; } = Standing.Freshman;

Membres de fonction expression-bodiedExpression-bodied function members

Le corps de beaucoup de membres que nous écrivons se compose d’une seule instruction qui peut être représentée sous la forme d’une expression.The body of a lot of members that we write consist of only one statement that can be represented as an expression. Vous pouvez réduire cette syntaxe en écrivant un membre expression-bodied à la place.You can reduce that syntax by writing an expression-bodied member instead. Cela fonctionne pour les méthodes et les propriétés en lecture seule.It works for methods and read-only properties. Par exemple, une substitution de ToString() est souvent un excellent candidat :For example, an override of ToString() is often a great candidate:

public override string ToString() => $"{LastName}, {FirstName}";

Vous pouvez également utiliser des membres expression-bodied dans des propriétés en lecture seule :You can also use expression-bodied members in read-only properties as well:

public string FullName => $"{FirstName} {LastName}";

using staticusing static

L’amélioration de using static vous permet d’importer les méthodes statiques d’une classe unique.The using static enhancement enables you to import the static methods of a single class. Auparavant, l’instruction using importait tous les types d’un espace de noms.Previously, the using statement imported all types in a namespace.

Souvent, nous utilisons les méthodes statiques d’une classe dans notre code.Often we use a class' static methods throughout our code. Saisir de manière répétée le nom de classe peut nuire à la visibilité de la signification de votre code.Repeatedly typing the class name can obscure the meaning of your code. C’est souvent le cas quand vous écrivez des classes qui effectuent de nombreux calculs numériques.A common example is when you write classes that perform many numeric calculations. Votre code sera encombré de Sin, de Sqrt et d’autres appels à différentes méthodes de la classe Math.Your code will be littered with Sin, Sqrt and other calls to different methods in the Math class. La nouvelle syntaxe using static clarifie considérablement la lecture de ces classes.The new using static syntax can make these classes much cleaner to read. Vous spécifiez la classe que vous utilisez :You specify the class you're using:

using static System.Math;

Vous pouvez maintenant utiliser n’importe quelle méthode statique de la classe Math sans qualifier la classe Math.And now, you can use any static method in the Math class without qualifying the Math class. La classe Math est un excellent cas d’usage de cette fonctionnalité, car elle ne contient aucune méthode d’instance.The Math class is a great use case for this feature because it does not contain any instance methods. Vous pouvez également utiliser using static pour importer les méthodes statiques d’une classe pour une classe qui comprend à la fois des méthodes statiques et des méthodes d’instance.You can also use using static to import a class' static methods for a class that has both static and instance methods. Un des exemples les plus utiles est String :One of the most useful examples is String:

using static System.String;

Note

Vous devez utiliser le nom de classe complet, System.String, dans une instruction static using.You must use the fully qualified class name, System.String in a static using statement. Vous ne pouvez pas utiliser le mot clé string à la place.You cannot use the string keyword instead.

Vous pouvez maintenant appeler des méthodes statiques définies dans la classe String sans qualifier ces méthodes comme membres de cette classe :You can now call static methods defined in the String class without qualifying those methods as members of that class:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

La fonctionnalité static using et les méthodes d’extension interagissent de façons intéressantes, et la conception du langage comprenait des règles qui traitent spécifiquement ces interactions.The static using feature and extension methods interact in interesting ways, and the language design included some rules that specifically address those interactions. L’objectif est de réduire les risques de changements importants dans des codes base existants, dont les vôtres.The goal is to minimize any chances of breaking changes in existing codebases, including yours.

Les méthodes d’extension sont dans la portée uniquement quand elles sont appelées à l’aide de la syntaxe d’invocation de méthode d’extension, et pas quand elles sont appelées en tant que méthode statique.Extension methods are only in scope when called using the extension method invocation syntax, not when called as a static method. Ce sera souvent le cas dans les requêtes LINQ.You'll often see this in LINQ queries. Vous pouvez importer le modèle LINQ en important Enumerable.You can import the LINQ pattern by importing Enumerable.

using static System.Linq.Enumerable;

Cette opération importe toutes les méthodes de la classe Enumerable.This imports all the methods in the Enumerable class. Toutefois, les méthodes d’extension sont dans la portée uniquement quand elles sont appelées en tant que méthodes d’extension.However, the extension methods are only in scope when called as extension methods. Elles ne sont pas dans la portée si elles sont appelées à l’aide de la syntaxe de méthode statique :They are not in scope if they are called using the static method syntax:

public bool MakesDeansList()
{
    return Grades.All(g => g > 3.5) && Grades.Any();
    // Code below generates CS0103: 
    // The name 'All' does not exist in the current context.
    //return All(Grades, g => g > 3.5) && Grades.Any();
}

Cette décision est justifiée par le fait que les méthodes d’extension sont généralement appelées à l’aide d’expressions d’invocation de méthode d’extension.This decision is because extension methods are typically called using extension method invocation expressions. Il existe de rares cas où elles sont appelées à l’aide de la syntaxe d’appel de méthode statique, dans le but de résoudre une ambiguïté.In the rare case where they are called using the static method call syntax it is to resolve ambiguity. Il semble judicieux d’exiger le nom de la classe dans le cadre de l’invocation.Requiring the class name as part of the invocation seems wise.

static using comprend une dernière fonctionnalité.There's one last feature of static using. La directive static using importe également n’importe quel type imbriqué.The static using directive also imports any nested types. Cela vous permet de référencer tout type imbriqué sans qualification.That enables you to reference any nested types without qualification.

Null - Opérateurs conditionnelNull-conditional operators

Les valeurs null compliquent le code.Null values complicate code. Vous devez vérifier chaque accès de variables afin de garantir que vous ne déréférencez pas null.You need to check every access of variables to ensure you are not dereferencing null. L’opérateur conditionnel null rend ces contrôles beaucoup plus faciles et plus fluides.The null conditional operator makes those checks much easier and fluid.

Remplacez simplement l’accès aux membres . par ?. :Simply replace the member access . with ?.:

var first = person?.FirstName; 

Dans l’exemple précédent, la valeur null est assignée à la variable first si l’objet person a la valeur null.In the preceding example, the variable first is assigned null if the person object is null. Sinon, la valeur de la propriété FirstName lui est assignée.Otherwise, it gets assigned the value of the FirstName property. Plus important encore, le ?. signifie que cette ligne de code ne génère pas de NullReferenceException quand la variable person a la valeur null.Most importantly, the ?. means that this line of code does not generate a NullReferenceException when the person variable is null. Au lieu de cela, elle court-circuite et produit null.Instead, it short-circuits and produces null.

Notez également que cette expression retourne un string, quelle que soit la valeur de person.Also, note that this expression returns a string, regardless of the value of person. Dans le cas d’un court-circuit, la valeur null retournée est typée pour correspondre à l’expression complète.In the case of short circuiting, the null value returned is typed to match the full expression.

Vous pouvez souvent utiliser cette construction avec l’opérateur de fusion null pour assigner des valeurs par défaut quand l’une des propriétés a la valeur null :You can often use this construct with the null coalescing operator to assign default values when one of the properties are null:

first = person?.FirstName ?? "Unspecified";

L’opérande de droite de l’opérateur ?. n’est pas limité aux propriétés ou aux champs.The right hand side operand of the ?. operator is not limited to properties or fields. Vous pouvez également l’utiliser pour appeler des méthodes de manière conditionnelle.You can also use it to conditionally invoke methods. L’utilisation la plus courante de fonctions membres avec l’opérateur conditionnel null consiste à appeler en toute sécurité des délégués (ou des gestionnaires d’événements) qui peuvent avoir la valeur null.The most common use of member functions with the null conditional operator is to safely invoke delegates (or event handlers) that may be null. Pour ce faire, appelez la méthode Invoke du délégué à l’aide de l’opérateur ?. pour accéder au membre.You'll do this by calling the delegate's Invoke method using the ?. operator to access the member. Vous trouverez un exemple dans la rubrique relative auxYou can see an example in the
modèles de délégués.delegate patterns topic.

Les règles de l’opérateur ?. garantissent que la partie gauche de l’opérateur n’est évalué qu’une seule fois.The rules of the ?. operator ensure that the left-hand side of the operator is evaluated only once. Cela est important et permet de nombreux idiomes, notamment l’exemple utilisant des gestionnaires d’événements.This is important and enables many idioms, including the example using event handlers. Commençons par l’utilisation de gestionnaires d’événements.Let's start with the event handler usage. Dans les précédentes versions de C#, vous étiez encouragé à écrire du code semblable à ceci :In previous versions of C#, you were encouraged to write code like this:

var handler = this.SomethingHappened;
if (handler != null)
    handler(this, eventArgs);

Cela était préférable à une syntaxe plus simple :This was preferred over a simpler syntax:

// Not recommended
if (this.SomethingHappened != null)
    this.SomethingHappened(this, eventArgs);

Important

L’exemple précédent introduit une concurrence critique.The preceding example introduces a race condition. L’événement SomethingHappened peut avoir des abonnés au moment où il est vérifié pour voir s’il a la valeur null, et ces abonnés peuvent avoir été supprimés avant le déclenchement de l’événement.The SomethingHappened event may have subscribers when checked against null, and those subscribers may have been removed before the event is raised. Cela entraînerait la levée d’un NullReferenceException.That would cause a NullReferenceException to be thrown.

Dans cette deuxième version, le gestionnaire d’événements SomethingHappened peut être non null au moment du test, mais si un autre code supprime un gestionnaire, il peut être encore null quand le gestionnaire d’événements a été appelé.In this second version, the SomethingHappened event handler might be non-null when tested, but if other code removes a handler, it could still be null when the event handler was called.

Le compilateur génère du code pour l’opérateur ?. qui garantit que la partie gauche (this.SomethingHappened) de l’expression ?. n’est évaluée qu’une seule fois, et le résultat est mis en cache :The compiler generates code for the ?. operator that ensures the left side (this.SomethingHappened) of the ?. expression is evaluated once, and the result is cached:

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

Le fait de garantir que la partie gauche n’est évaluée qu’une seule fois vous permet également d’utiliser n’importe quelle expression, notamment des appels de méthode, dans la partie gauche de l’expression ?.. Même si ces expressions ont des effets secondaires, elles ne sont évaluées qu’une seule fois ; les effets secondaires ne se produisent donc qu’une seule fois.Ensuring that the left side is evaluated only once also enables you to use any expression, including method calls, on the left side of the ?. Even if these have side-effects, they are evaluated once, so the side effects occur only once. Vous trouverez un exemple dans notre contenu relatif aux événements.You can see an example in our content on events.

Interpolation de chaîneString Interpolation

C# 6 contient une nouvelle syntaxe pour la composition de chaînes à partir d’une chaîne de format et des expressions qui sont évaluées pour produire d’autres valeurs de chaîne.C# 6 contains new syntax for composing strings from a format string and expressions that are evaluated to produce other string values.

Habituellement, vous deviez utiliser des paramètres de position dans une méthode telle que string.Format :Traditionally, you needed to use positional parameters in a method like string.Format:

public string FullName
{
    get
    {
        return string.Format("{0} {1}", FirstName, LastName);
    }
}

Avec C# 6, la nouvelle fonctionnalité d’interpolation de chaîne vous permet d’incorporer des expressions dans la chaîne de format.With C# 6, the new string interpolation feature enables you to embed the expressions in the format string. Faites simplement précéder la chaîne de $ :Simply preface the string with $:

public string FullName => $"{FirstName} {LastName}";

Cet exemple initial utilise des expressions de propriété pour les expressions substituées.This initial example uses property expressions for the substituted expressions. Vous pouvez étendre cette syntaxe pour utiliser n’importe quelle expression.You can expand on this syntax to use any expression. Par exemple, vous pourrez calculer la moyenne pondérée cumulative d’un étudiant dans le cadre de l’interpolation :For example, you could compute a student's grade point average as part of the interpolation:

public string GetFormattedGradePoint() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average()}";

En exécutant l’exemple précédent, vous pouvez voir que la sortie de Grades.Average() peut avoir plus de décimales que vous ne le souhaitez.Running the preceding example, you would find that the output for Grades.Average() might have more decimal places than you would like. La syntaxe d’interpolation de chaîne prend en charge toutes les chaînes de format disponibles en utilisant des méthodes de mise en forme précédentes.The string interpolation syntax supports all the format strings available using earlier formatting methods. Vous ajoutez les chaînes de format entre les accolades.You add the format strings inside the braces. Ajoutez un : après l’expression à mettre en forme :Add a : following the expression to format:

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

La ligne de code précédente met en forme la valeur de Grades.Average() sous la forme d’un nombre à virgule flottante à deux décimales.The preceding line of code formats the value for Grades.Average() as a floating-point number with two decimal places.

Le : est toujours interprété comme le séparateur entre l’expression mise en forme et la chaîne de format.The : is always interpreted as the separator between the expression being formatted and the format string. Cela peut entraîner des problèmes quand votre expression utilise un : d’une autre manière, par exemple comme opérateur conditionnel :This can introduce problems when your expression uses a : in another way, such as a conditional operator:

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Any() ? Grades.Average() : double.NaN:F2}";

Dans l’exemple précédent, le : est analysé comme début de la chaîne de format, et non comme partie de l’opérateur conditionnel.In the preceding example, the : is parsed as the beginning of the format string, not part of the conditional operator. Dans tous les cas où cela se produit, vous pouvez mettre l’expression entre parenthèses pour forcer le compilateur à interpréter l’expression comme vous le souhaitez :In all cases where this happens, you can surround the expression with parentheses to force the compiler to interpret the expression as you intend:

public string GetGradePointPercentages() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {(Grades.Any() ? Grades.Average() : double.NaN):F2}";

Aucune limite ne s’applique aux expressions que vous pouvez placer entre accolades.There aren't any limitations on the expressions you can place between the braces. Vous pouvez exécuter une requête LINQ complexe à l’intérieur d’une chaîne interpolée pour effectuer des calculs et afficher le résultat :You can execute a complex LINQ query inside an interpolated string to perform computations and display the result:

public string GetAllGrades() =>
    $@"All Grades: {Grades.OrderByDescending(g => g)
    .Select(s => s.ToString("F2")).Aggregate((partial, element) => $"{partial}, {element}")}";

Dans cet exemple, vous pouvez voir qu’il est même possible d’imbriquer une expression d’interpolation de chaîne à l’intérieur d’une autre expression d’interpolation de chaîne.You can see from this sample that you can even nest a string interpolation expression inside another string interpolation expression. Cet exemple est très probablement plus complexe que vous ne le souhaiteriez dans le code de production.This example is very likely more complex than you would want in production code. En fait, il illustre l’étendue de la fonctionnalité.Rather, it is illustrative of the breadth of the feature. Toute expression C# peut être placée entre les accolades d’une chaîne interpolée.Any C# expression can be placed between the curly braces of an interpolated string.

Interpolation de chaîne et cultures spécifiquesString interpolation and specific cultures

Tous les exemples présentés dans la section précédente mettent en forme les chaînes à l’aide de la culture et de la langue actuelles sur l’ordinateur sur lequel le code s’exécute.All the examples shown in the preceding section format the strings using the current culture and language on the machine where the code executes. Souvent, vous devrez mettre en forme la chaîne produite à l’aide d’une culture spécifique.Often you may need to format the string produced using a specific culture. Pour cela, utilisez le fait que l’objet résultant d’une interpolation de chaîne peut être converti implicitement en FormattableString.To do that use the fact that the object produced by a string interpolation can be implicitly converted to FormattableString.

L’instance FormattableString contient la chaîne de format et les résultats de l’évaluation des expressions avant leur conversion en chaînes.The FormattableString instance contains the format string, and the results of evaluating the expressions before converting them to strings. Vous pouvez utiliser des méthodes publiques de FormattableString pour spécifier la culture lors de la mise en forme d’une chaîne.You can use public methods of FormattableString to specify the culture when formatting a string. Par exemple, l’exemple suivant produit une chaîne en utilisant la culture allemande.For example, the following example produces a string using German culture. (Il utilise le caractère « , » comme séparateur décimal et le caractère « . » comme séparateur des milliers.)(It uses the ',' character for the decimal separator, and the '.' character as the thousands separator.)

FormattableString str = $"Average grade is {s.Grades.Average()}";
var gradeStr = str.ToString(new System.Globalization.CultureInfo("de-DE"));

Pour plus d’informations, consultez la rubrique Interpolation de chaîne.For more information, see the String interpolation topic.

Filtres d’exceptionException Filters

Les filtres d’exception constituent une autre nouvelle fonctionnalité en C# 6.Another new feature in C# 6 is exception filters. Les filtres d’exception sont des clauses qui déterminent quand une clause catch donnée doit être appliquée.Exception Filters are clauses that determine when a given catch clause should be applied. Si l’expression utilisée pour un filtre d’exception prend la valeur true, la clause catch effectue son traitement normal sur une exception.If the expression used for an exception filter evaluates to true, the catch clause performs its normal processing on an exception. Si l’expression prend la valeur false, la clause catch est ignorée.If the expression evaluates to false, then the catch clause is skipped.

Une de leurs utilisations consiste à examiner les informations concernant une exception pour déterminer si une clause catch peut la traiter :One use is to examine information about an exception to determine if a catch clause can process the exception:

public static async Task<string> MakeRequest()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    }
}

Le code généré par les filtres d’exception fournit de meilleures informations concernant une exception qui est levée et non traitée.The code generated by exception filters provides better information about an exception that is thrown and not processed. Avant que des filtres d’exception soient ajoutés au langage, vous deviez créer du code semblable au code suivant :Before exception filters were added to the language, you would need to create code like the following:

public static async Task<string> MakeRequest()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e)
    {
        if (e.Message.Contains("301"))
            return "Site Moved";
        else
            throw;
    }
}

Le point où l’exception est levée change entre ces deux exemples.The point where the exception is thrown changes between these two examples. Dans le code précédent, où une clause throw est utilisée, toute analyse de trace de pile ou tout examen de vidages sur incident montrera que l’exception a été levée à partir de l’instruction throw dans votre clause catch.In the previous code, where a throw clause is used, any stack trace analysis or examination of crash dumps will show that the exception was thrown from the throw statement in your catch clause. L’objet exception réel contiendra la pile des appels d’origine, mais toutes les autres informations relatives aux variables de la pile des appels entre ce point de levée et l’emplacement du point de levée d’origine ont été perdues.The actual exception object will contain the original call stack, but all other information about any variables in the call stack between this throw point and the location of the original throw point has been lost.

Comparez cela à la façon dont le code qui utilise un filtre d’exception est traité : l’expression de filtre d’exception prend la valeur false.Contrast that with how the code using an exception filter is processed: the exception filter expression evaluates to false. Par conséquent, l’exécution n’entre jamais dans la clause catch.Therefore, execution never enters the catch clause. Étant donné que la clause catch ne s’exécute pas, aucun déroulement de pile n’a lieu.Because the catch clause does not execute, no stack unwinding takes place. Cela signifie que l’emplacement de levée d’origine est conservé pour toutes les activités de débogage qui se produiront ultérieurement.That means the original throw location is preserved for any debugging activities that would take place later.

Chaque fois que vous avez besoin d’évaluer des champs ou des propriétés d’une exception, au lieu de vous appuyer uniquement sur le type d’exception, utilisez un filtre d’exception pour conserver davantage d’informations de débogage.Whenever you need to evaluate fields or properties of an exception, instead of relying solely on the exception type, use an exception filter to preserve more debugging information.

Une autre pratique recommandée avec des filtres d’exception consiste à les utiliser pour les routines de journalisation.Another recommended pattern with exception filters is to use them for logging routines. Cette utilisation profite également de la manière dont le point de levée de l’exception est conservé quand un filtre d’exception prend la valeur false.This usage also leverages the manner in which the exception throw point is preserved when an exception filter evaluates to false.

Une méthode de journalisation est une méthode dont l’argument est l’exception qui retourne false sans condition :A logging method would be a method whose argument is the exception that unconditionally returns false:

public static bool LogException(this Exception e)
{
    Console.Error.WriteLine($"Exceptions happen: {e}");
    return false;
} 

Chaque fois que vous voulez journaliser une exception, vous pouvez ajouter une clause catch et utiliser cette méthode comme filtre d’exception :Whenever you want to log an exception, you can add a catch clause, and use this method as the exception filter:

public void MethodThatFailsSometimes()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
} 

Les exceptions ne sont jamais interceptées, car la méthode LogException retourne toujours false.The exceptions are never caught, because the LogException method always returns false. Ce filtre d’exception qui a toujours la valeur false signifie que vous pouvez placer ce gestionnaire de journalisation avant tous les autres gestionnaires d’exceptions :That always false exception filter means that you can place this logging handler before any other exception handlers:

public void MethodThatFailsButHasRecoveryPath()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
    catch (RecoverableException ex)
    {
        Console.WriteLine(ex.ToString());
        // This can still catch the more specific
        // exception because the exception filter
        // above always returns false.
        // Perform recovery here 
    }
}

L’exemple précédent met en évidence une facette très importante des filtres d’exception.The preceding example highlights a very important facet of exception filters. Ces derniers autorisent les scénarios où une clause catch d’exception plus générale peut apparaître avant une clause plus spécifique.The exception filters enable scenarios where a more general exception catch clause may appear before a more specific one. Il est également possible que le même type d’exception apparaisse dans plusieurs clauses catch :It's also possible to have the same exception type appear in multiple catch clauses:

public static async Task<string> MakeRequestWithNotModifiedSupport()
{ 
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        return "Site Moved";
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("304"))
    {
        return "Use the Cache";
    }
}

Une autre pratique recommandée empêche que les clauses catch traitent les exceptions quand un débogueur est attaché.Another recommended pattern helps prevent catch clauses from processing exceptions when a debugger is attached. Cette technique vous permet d’exécuter une application avec le débogueur, et d’arrêter son exécution quand une exception est levée.This technique enables you to run an application with the debugger, and stop execution when an exception is thrown.

Dans votre code, ajoutez un filtre d’exception afin que tout code de récupération s’exécute uniquement quand un débogueur n’est pas attaché :In your code, add an exception filter so that any recovery code executes only when a debugger is not attached:

public void MethodThatFailsWhenDebuggerIsNotAttached()
{
    try {
        PerformFailingOperation();
    } catch (Exception e) when (e.LogException())
    {
        // This is never reached!
    }
    catch (RecoverableException ex) when (!System.Diagnostics.Debugger.IsAttached)
    {
        Console.WriteLine(ex.ToString());
        // Only catch exceptions when a debugger is not attached.
        // Otherwise, this should stop in the debugger. 
    }
}

Après avoir ajouté cela au code, vous configurez votre débogueur pour qu’il s’arrête sur toutes les exceptions non prises en charge.After adding this in code, you set your debugger to break on all unhandled exceptions. Exécutez le programme sous le débogueur. Ce dernier s’arrête chaque fois que PerformFailingOperation() lève un RecoverableException.Run the program under the debugger, and the debugger breaks whenever PerformFailingOperation() throws a RecoverableException. Le débogueur arrête votre programme, car la clause catch ne sera pas exécutée en raison du filtre d’exception qui retourne false.The debugger breaks your program, because the catch clause won't be executed due to the false-returning exception filter.

Expressions nameofnameof Expressions

L’expression nameof prend comme valeur le nom d’un symbole.The nameof expression evaluates to the name of a symbol. C’est un très bon moyen d’obtenir les outils fonctionnant chaque fois que vous avez besoin du nom d’une variable, d’une propriété ou d’un champ de membre.It's a great way to get tools working whenever you need the name of a variable, a property, or a member field.

L’une des utilisations les plus courantes de nameof est la fourniture du nom d’un symbole qui a provoqué une exception :One of the most common uses for nameof is to provide the name of a symbol that caused an exception:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

Une autre utilisation concerne les applications XAML qui implémentent l’interface INotifyPropertyChanged :Another use is with XAML based applications that implement the INotifyPropertyChanged interface:

public string LastName
{
    get { return lastName; }
    set
    {
        if (value != lastName)
        {
            lastName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

L’avantage d’utiliser l’opérateur nameof sur une chaîne constante est que les outils peuvent comprendre le symbole.The advantage of using the nameof operator over a constant string is that tools can understand the symbol. Si vous utilisez des outils de refactorisation pour renommer le symbole, il sera renommé dans l’expression nameof.If you use refactoring tools to rename the symbol, it will rename it in the nameof expression. Les chaînes constantes ne présentent pas cet avantage.Constant strings don't have that advantage. Faites vous-même l’essai dans votre éditeur préféré : renommez une variable ; toutes les expressions nameof se mettront également à jour.Try it yourself in your favorite editor: rename a variable, and any nameof expressions will update as well.

L’expression nameof produit le nom non qualifié de son argument (LastName dans les exemples précédents), même si vous utilisez le nom complet de l’argument :The nameof expression produces the unqualified name of its argument (LastName in the previous examples) even if you use the fully qualified name for the argument:

public string FirstName
{
    get { return firstName; }
    set
    {
        if (value != firstName)
        {
            firstName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(UXComponents.ViewModel.FirstName)));
        }
    }
}
private string firstName;

Cette expression nameof produit FirstName, et non pas UXComponents.ViewModel.FirstName.This nameof expression produces FirstName, not UXComponents.ViewModel.FirstName.

await dans des blocs catch et finallyAwait in Catch and Finally blocks

C# 5 présentait plusieurs restrictions concernant les emplacements où vous pouviez placer des expressions await.C# 5 had several limitations around where you could place await expressions. L’une de ces restrictions a été supprimée dans C# 6.One of those has been removed in C# 6. Vous pouvez maintenant utiliser await dans des expressions catch ou finally.You can now use await in catch or finally expressions.

L’ajout d’expressions await dans des blocs catch et finally peut sembler compliquer la façon dont elles sont traitées.The addition of await expressions in catch and finally blocks may appear to complicate how those are processed. Ajoutons un exemple pour étudier comment cela se présente.Let's add an example to discuss how this appears. Dans n’importe quelle méthode asynchrone, vous pouvez utiliser une expression await dans une clause finally.In any async method, you can use an await expression in a finally clause.

Avec C# 6, vous pouvez également utiliser await dans des expressions catch.With C# 6, you can also await in catch expressions. C’est le plus souvent le cas avec les scénarios de journalisation :This is most often used with logging scenarios:

public static async Task<string> MakeRequestAndLogFailures()
{ 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

Les informations d’implémentation détaillées de l’ajout de la prise en charge d’await dans des clauses catch et finally garantissent que le comportement est cohérent avec le comportement du code synchrone.The implementation details for adding await support inside catch and finally clauses ensures that the behavior is consistent with the behavior for synchronous code. Quand le code exécuté dans une clause catch ou finally est déclenché, l’exécution recherche une clause catch appropriée dans le bloc voisin suivant.When code executed in a catch or finally clause throws, execution looks for a suitable catch clause in the next surrounding block. S’il existait une exception actuelle, elle est perdue.If there was a current exception, that exception is lost. Il en va de même pour les expressions await dans les clauses catch et finally : une clause catch appropriée est recherchée, et l’exception actuelle, le cas échéant, est perdue.The same happens with awaited expressions in catch and finally clauses: a suitable catch is searched for, and the current exception, if any, is lost.

Note

De ce fait, il est recommandé d’écrire les clauses catch et finally avec précaution, afin d’éviter d’introduire de nouvelles exceptions.This behavior is the reason it's recommended to write catch and finally clauses carefully, to avoid introducing new exceptions.

Initialiseurs d’index.Index Initializers

Les initialiseurs d’index constituent l’une des deux fonctionnalités qui améliorent la cohérence des initialiseurs.Index Initializers is one of two features that make collection initializers more consistent. Dans les versions Release précédentes de C#, vous ne pouviez utiliser des initialiseurs de collection qu’avec des collections de styles de séquence :In earlier releases of C#, you could use collection initializers only with sequence style collections:

private List<string> messages = new List<string> 
{
    "Page not Found",
    "Page moved, but left a forwarding address.",
    "The web server can't come out to play today."
};

À présent, vous pouvez également les utiliser avec des collections Dictionary<TKey,TValue> et des types similaires :Now, you can also use them with Dictionary<TKey,TValue> collections and similar types:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

Cette fonctionnalité signifie que les conteneurs associatifs peuvent être initialisés à l’aide d’une syntaxe similaire à ce qui a été mis en place pour les conteneurs de séquence depuis plusieurs versions.This feature means that associative containers can be initialized using syntax similar to what's been in place for sequence containers for several versions.

Méthodes Add d’extension dans des initialiseurs de collectionsExtension Add methods in collection initializers

La possibilité d’utiliser une méthode d’extension pour la méthode Add constitue une autre fonctionnalité qui simplifie l’initialisation des collections.Another feature that makes collection initialization easier is the ability to use an extension method for the Add method. Elle a été ajoutée à des fins de parité avec Visual Basic.This feature was added for parity with Visual Basic.

Cette fonctionnalité est particulièrement utile pour ajouter sémantiquement de nouveaux éléments quand vous avez une classe de collection personnalisée dont une méthode porte un nom différent.The feature is most useful when you have a custom collection class that has a method with a different name to semantically add new items.

Prenons, par exemple, une collection d’étudiants telle que la suivante :For example, consider a collection of students like this:

public class Enrollment : IEnumerable<Student>
{
    private List<Student> allStudents = new List<Student>();

    public void Enroll(Student s)
    {
        allStudents.Add(s);
    }

    public IEnumerator<Student> GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }
}

La méthode Enroll ajoute un étudiant.The Enroll method adds a student. Mais elle ne suit pas le modèle Add.But it doesn't follow the Add pattern. Dans les versions antérieures de C#, vous ne pouviez pas utiliser d’initialiseurs de collections avec un objet Enrollment :In previous versions of C#, you could not use collection initializers with an Enrollment object:

var classList = new Enrollment()
{
    new Student("Lessie", "Crosby"),
    new Student("Vicki", "Petty"),
    new Student("Ofelia", "Hobbs"),
    new Student("Leah", "Kinney"),
    new Student("Alton", "Stoker"),
    new Student("Luella", "Ferrell"),
    new Student("Marcy", "Riggs"),
    new Student("Ida", "Bean"),
    new Student("Ollie", "Cottle"),
    new Student("Tommy", "Broadnax"),
    new Student("Jody", "Yates"),
    new Student("Marguerite", "Dawson"),
    new Student("Francisca", "Barnett"),
    new Student("Arlene", "Velasquez"),
    new Student("Jodi", "Green"),
    new Student("Fran", "Mosley"),
    new Student("Taylor", "Nesmith"),
    new Student("Ernesto", "Greathouse"),
    new Student("Margret", "Albert"),
    new Student("Pansy", "House"),
    new Student("Sharon", "Byrd"),
    new Student("Keith", "Roldan"),
    new Student("Martha", "Miranda"),
    new Student("Kari", "Campos"),
    new Student("Muriel", "Middleton"),
    new Student("Georgette", "Jarvis"),
    new Student("Pam", "Boyle"),
    new Student("Deena", "Travis"),
    new Student("Cary", "Totten"),
    new Student("Althea", "Goodwin")
};

C’est maintenant possible, mais uniquement si vous créez une méthode d’extension qui mappe Add à Enroll :Now you can, but only if you create an extension method that maps Add to Enroll:

public static class StudentExtensions
{
    public static void Add(this Enrollment e, Student s) => e.Enroll(s);
}

En utilisant cette fonctionnalité, vous mappez toute méthode ajoutant des éléments à une collection à une méthode nommée Add en créant une méthode d’extension :What you are doing with this feature is to map whatever method adds items to a collection to a method named Add by creating an extension method:

public class Enrollment : IEnumerable<Student>
{
    private List<Student> allStudents = new List<Student>();

    public void Enroll(Student s)
    {
        allStudents.Add(s);
    }

    public IEnumerator<Student> GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<Student>)allStudents).GetEnumerator();
    }
}
public class ClassList
{
    public Enrollment CreateEnrollment()
    {
        var classList = new Enrollment()
        {
            new Student("Lessie", "Crosby"),
            new Student("Vicki", "Petty"),
            new Student("Ofelia", "Hobbs"),
            new Student("Leah", "Kinney"),
            new Student("Alton", "Stoker"),
            new Student("Luella", "Ferrell"),
            new Student("Marcy", "Riggs"),
            new Student("Ida", "Bean"),
            new Student("Ollie", "Cottle"),
            new Student("Tommy", "Broadnax"),
            new Student("Jody", "Yates"),
            new Student("Marguerite", "Dawson"),
            new Student("Francisca", "Barnett"),
            new Student("Arlene", "Velasquez"),
            new Student("Jodi", "Green"),
            new Student("Fran", "Mosley"),
            new Student("Taylor", "Nesmith"),
            new Student("Ernesto", "Greathouse"),
            new Student("Margret", "Albert"),
            new Student("Pansy", "House"),
            new Student("Sharon", "Byrd"),
            new Student("Keith", "Roldan"),
            new Student("Martha", "Miranda"),
            new Student("Kari", "Campos"),
            new Student("Muriel", "Middleton"),
            new Student("Georgette", "Jarvis"),
            new Student("Pam", "Boyle"),
            new Student("Deena", "Travis"),
            new Student("Cary", "Totten"),
            new Student("Althea", "Goodwin")
        };
        return classList;
    }           
}

public static class StudentExtensions
{
    public static void Add(this Enrollment e, Student s) => e.Enroll(s);
}

Résolution de surcharge amélioréeImproved overload resolution

Il est probable que vous ne remarquerez pas cette dernière fonctionnalité.This last feature is one you probably won't notice. La précédente version du compilateur C# pouvait trouver, dans certaines constructions, des appels de méthode impliquant des expressions lambda ambigües.There were constructs where the previous version of the C# compiler may have found some method calls involving lambda expressions ambiguous. Prenons la méthode suivante :Consider this method:

static Task DoThings() 
{
     return Task.FromResult(0); 
}

Dans les versions antérieures de C#, l’appel de cette méthode à l’aide de la syntaxe de groupe de méthodes échouait :In earlier versions of C#, calling that method using the method group syntax would fail:

Task.Run(DoThings); 

Le précédent compilateur ne pouvait pas faire correctement la distinction entre Task.Run(Action) et Task.Run(Func<Task>()).The earlier compiler could not distinguish correctly between Task.Run(Action) and Task.Run(Func<Task>()). Dans les versions précédentes, vous deviez utiliser une expression lambda comme argument :In previous versions, you'd need to use a lambda expression as an argument:

Task.Run(() => DoThings());

Le compilateur C# 6 détermine correctement que Task.Run(Func<Task>()) constitue un meilleur choix.The C# 6 compiler correctly determines that Task.Run(Func<Task>()) is a better choice.