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. 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 apprenant bien ces fonctionnalités, vous gagnez en productivité et écrivez du code plus lisible.Learn these features well, and you'll be more productive and write more readable code. Vous pouvez également vous concentrer davantage sur vos fonctionnalités que sur les constructions du langage.You can concentrate more on your features than on the constructs of the language.

La suite de cet article fournit une vue d’ensemble de ces fonctionnalités, avec des liens pour explorer chacune d’entre elles.The rest of this article provides an overview of each of these features, with a link to explore each feature. Vous pouvez également explorer les fonctionnalités dans une exploration interactive sur C# 6 dans la section des tutoriels.You can also explore the features in an interactive exploration on C# 6 in the tutorials section.

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. 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 utilise la syntaxe d’auto-property plus concise et plus pratique.This feature enables true language support for creating immutable types and uses the more concise and convenient auto-property syntax.

Si l’ajout de cette syntaxe ne supprime pas une méthode accessible, il s’agit d’un changement compatible binaire.If adding this syntax doesn't remove an accessible method, it's a binary compatible change.

Initialiseurs de propriétés automatiquesAuto-property initializers

Les initialiseurs de propriétés automatiques vous permettent de déclarer la valeur initiale d’une propriété automatique 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.

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's 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 the public interface for Student objects.

Membres de fonction expression-bodiedExpression-bodied function members

De nombreux membres que vous écrivez sont des instructions uniques qui pourraient être des expressions simples.Many members that you write are single statements that could be single expressions. Écrivez un membre expression-bodied à la place.Write 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 cette syntaxe pour les propriétés en lecture seule :You can also use this syntax for read-only properties:

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

La modification d’un membre existant en un membre expression-bodied est une modification compatible binaire.Changing an existing member to an expression bodied member is a binary compatible change.

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. Vous spécifiez la classe que vous utilisez :You specify the class you're using:

using static System.Math;

Math ne contient aucune méthode d’instance.The Math 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;

Notes

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.

Quand elles sont importées à partir d’une instruction static using, les méthodes d’extension ne sont dans la portée que si elles sont appelées à l’aide de la syntaxe d’invocation de méthode d’extension.When imported from a static using statement, extension methods are only in scope when called using the extension method invocation syntax. Elle ne sont pas dans la portée si elles sont appelées en tant que méthode statique.They aren't in scope 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 ou Queryable.You can import the LINQ pattern by importing Enumerable, or Queryable.

using static System.Linq.Enumerable;

Vous appelez généralement les méthodes d’extension à l’aide d’expressions d’appel de méthode d’extension.You typically call extension methods using extension method invocation expressions. Pour lever toute ambiguïté, ajoutez le nom de la classe dans les rares cas où vous les appelez à l’aide de la syntaxe d’appel de méthode statique.Adding the class name in the rare case where you call them using static method call syntax resolves ambiguity.

La directive static using importe également n’importe quel type imbriqué.The static using directive also imports any nested types. Vous pouvez référencer tous les types imbriqués sans qualification.You can reference any nested types without qualification.

Null - Opérateurs conditionnelNull-conditional operators

L’opérateur conditionnel null rend ces vérifications de valeur null beaucoup plus faciles et plus fluides.The null conditional operator makes null checks much easier and fluid. Remplacez l’accès aux membres . par ?. :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 is assigned the value of the FirstName property. Plus important encore, le ?. signifie que cette ligne de code ne génère pas de NullReferenceException si la variable person a la valeur null.Most importantly, the ?. means that this line of code doesn't generate a NullReferenceException if the person variable is null. Au lieu de cela, elle court-circuite et retourne null.Instead, it short-circuits and returns null. Vous pouvez également utiliser un opérateur conditionnel null pour l’accès au tableau ou à l’indexeur.You can also use a null conditional operator for array or indexer access. Remplacez [] par ?[] dans l’expression d’index.Replace [] with ?[] in the index expression.

L’expression suivante retourne un string, quelle que soit la valeur de person.The following expression returns a string, regardless of the value of person. Vous pouvez 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 often use this construct with the null coalescing operator to assign default values when one of the properties is null. Quand l’expression court-circuite, la valeur null retournée est typée pour correspondre à l’expression complète.When the expression short-circuits, the null value returned is typed to match the full expression.

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

Vous pouvez également utiliser ?. pour appeler des méthodes de manière conditionnelle.You can also use ?. 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. Appelez la méthode Invoke du délégué à l’aide de l’opérateur ?. pour accéder au membre.You'll call the delegate's Invoke method using the ?. operator to access the member. Vous trouverez un exemple dans l’article relatif aux modèles de délégués.You can see an example in the delegate patterns article.

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. De nombreux idiomes sont permis, notamment l’exemple suivant qui utilise des gestionnaires d’événements :It enables many idioms, including the following example using event handlers:

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

Le fait de vérifier que le côté gauche n’est évalué qu’une seule fois permet également d’utiliser n’importe quelle expression, notamment des appels de méthode, à gauche deEnsuring that the left side is evaluated only once also enables you to use any expression, including method calls, on the left side of the ?.

Interpolation de chaîneString interpolation

Avec C# 6, la nouvelle fonctionnalité d’interpolation de chaîne vous permet d’incorporer des expressions dans une chaîne.With C# 6, the new string interpolation feature enables you to embed expressions in a string. Il vous suffit de faire précéder la chaîne de $ et d’utiliser des expressions entre { et } à la place d’ordinaux :Simply preface the string with $and use expressions between { and } instead of ordinals:

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

Cet exemple utilise des propriétés pour les expressions substituées.This example uses properties for the substituted expressions. Vous pouvez utiliser n’importe quelle expression.You can 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 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.

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. Vous utilisez le fait que l’objet résultant d’une interpolation de chaîne peut être converti implicitement en System.FormattableString.You use the fact that the object produced by a string interpolation can be implicitly converted to System.FormattableString. L’instance FormattableString contient la chaîne de format composite et les résultats de l’évaluation des expressions avant leur conversion en chaînes.The FormattableString instance contains the composite format string and the results of evaluating the expressions before converting them to strings. Utilisez la méthode FormattableString.ToString(IFormatProvider) pour spécifier la culture lors de la mise en forme d’une chaîne.Use the FormattableString.ToString(IFormatProvider) method to specify the culture when formatting a string. L’exemple suivant produit une chaîne en utilisant la culture allemande (de-DE).The following example produces a string using the German (de-DE) culture. (Par défaut, la culture allemande utilise le caractère « , » comme séparateur décimal et « . » comme séparateur des milliers.)(By default, the German culture 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 bien démarrer avec l’interpolation de chaîne, consultez le tutoriel interactif Interpolation de chaîne en C#, l’article Interpolation de chaîne et le tutoriel Interpolation de chaîne en C#.To get started with string interpolation, see the String interpolation in C# interactive tutorial, the String interpolation article, and the String interpolation in C# tutorial.

Filtres d’exceptionsException 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()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

Expression nameofThe nameof expression

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;

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. Avec C# 6, vous pouvez maintenant utiliser await dans des expressions catch ou finally.With C# 6, you can now use await in catch or finally 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 détails d’implémentation relatifs à l’ajout de la prise en charge d’await dans des clauses catch et finally garantissent que le comportement est cohérent avec celui du code synchrone.The implementation details for adding await support inside catch and finally clauses ensure 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.

Notes

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.

Initialiser des collections associatives à l’aide d’indexeursInitialize associative collections using indexers

Les initialiseurs d’index constituent l’une des deux fonctionnalités qui améliorent la cohérence des initialiseurs de collection avec l’utilisation des index.Index Initializers is one of two features that make collection initializers more consistent with index usage. Dans les versions précédentes de C#, vous pouviez utiliser des initialiseurs de collection avec des collections de styles de séquence, notamment Dictionary<TKey,TValue>, en ajoutant des accolades autour des paires clé-valeur :In earlier releases of C#, you could use collection initializers with sequence style collections, including Dictionary<TKey,TValue>, by adding braces around key and value pairs:

private Dictionary<int, string> messages = 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."}
};

Vous pouvez les utiliser avec des collections Dictionary<TKey,TValue> et d’autres types où la méthode Add accessible accepte plusieurs arguments.You can use them with Dictionary<TKey,TValue> collections and other types where the accessible Add method accepts more than one argument. La nouvelle syntaxe prend en charge l’affectation à l’aide d’un index dans la collection :The new syntax supports assignment using an index into the collection:

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.

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 compilateur précédent ne pouvait pas faire correctement la distinction entre Task.Run(Action) et Task.Run(Func<Task>()).The earlier compiler couldn't 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.

Sortie du compilateur DeterministicDeterministic compiler output

L’option -deterministic fait en sorte que le compilateur génère un assembly de sortie identique octet par octet pour les compilations successives d’un même fichier source.The -deterministic option instructs the compiler to produce a byte-for-byte identical output assembly for successive compilations of the same source files.

Par défaut, chaque compilation génère une sortie unique.By default, every compilation produces unique output on each compilation. Le compilateur ajoute un horodateur et un GUID généré à partir de nombres aléatoires.The compiler adds a timestamp, and a GUID generated from random numbers. Vous utilisez cette option si vous souhaitez comparer la sortie octet par octet afin de garantir la cohérence entre les builds.You use this option if you want to compare the byte-for-byte output to ensure consistency across builds.

Pour plus d’informations, consultez l’article Option du compilateur -deterministic.For more information, see the -deterministic compiler option article.