Les nouveautés Microsoft C# 7.0

Jerome

Cette nouvelle version Microsoft de C# accompagne la sortie Visual Studio 2017. Les changements semblent minimes comparé au C#6.0 mais cette version améliore l’écriture du code et les performances.

Les nouvelles fonctionnalités au langage sont :

1.    Variables out

La syntaxe existante qui prend en charge les paramètres out a été améliorée dans cette version. Il n’est plus nécessaire de déclarer la variable en amont. :

Avant:

int numericResult;

if (int.TryParse(input, out numericResult))

WriteLine(numericResult);

Après:

if (int.TryParse(input, out int result))

WriteLine(result).

2.    Tuples

Il est désormais possible de créer des types légers et sans noms qui contiennent plusieurs champs publics sans avoir à utiliser les structures de données suivantes :

  • un tableau ou une liste ;
  • des paramètres out ;
  • la classe Tuple<…> existe depuis C# 4.0 ;
  • une classe spécifique pour représenter les résultats de la fonction.

Voici un exemple ecrit en C# 6.0:

public void Main()

{

var customer = GetCustomer();

int id = customer.Item1;

string firstname = customer.Item2;

string lastname = customer.Item3;

}

private Tuple<int, string, string> GetCustomer()

{

return new Tuple<int, string,=”” string=””>(1, “John”, “Doe”);

}

Avec C# 7.0, la méthode GetCustomer() devient:

private (int, string, string) GetCustomer()

{

return (1, “John”, “Doe”);

}

Au lieu d’utiliser .Item1, .Item2 nous pouvons nommer les propriétés du tuple:

public void Main()

{

var customer = GetCustomer2();

int id = customer.Id;

string firstname = customer. Firstname;

string lastname = customer. Lastname;

}

private (int Id, string Firstname, string Lastname) GetCustomer2()

{

return (1, “John”, “Doe”);

}

en décomposant le tuple directement en variables nous obtenons:

var (id, firstname, lastname) = GetCustomer();

 Par exemple, pour échanger les valeurs de deux variables, l’écriture devient triviale car nous n’avons plus besoin de variables intermédiaires :

(x, y) = (y, x).

3.    Éléments ignorés

Par exemple lors de la déconstruction d’un tuple, seul les variables nommées seront assignées:

var (_, _, _, pop1, _, pop2) = QueryCityDataForYears(“New York City”, 1960, 2010).

4.    Critères spéciaux

L’expression is étend l’opérateur is classique pour interroger un objet au-delà de son type. Dans notre exemple nous utilisons le « switch » mais fonctionne également avec le « if statement » :

public static int DiceSum(IEnumerable <object> values}

{

var sum = 0;

foreach (var item in values)

{

switch (item)

{

case 0:

break;

case int val:

sum += val;

break;

case IEnumerable<object> subList when subList.Any():

sum += DiceSum(subList);

break;

case IEnumerable<object> subList:

break;

case null:

break;

default:

throw new InvalidOperationException(“unknown item type”);

}

return sum;

}

5.    Variables locales et retours ref

Les arguments et les variables locales de méthode peuvent être des références à un autre stockage.

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”);

}

Maintenant que la méthode retourne une référence à la valeur entière dans la matrice, vous devez modifier l’emplacement où elle est appelée. La déclaration var signifie que valItem est une référence vers un int :

var valItem = MatrixSearch.Find3(matrix, (val) => val == 42);

Console.WriteLine(valItem);

valItem = 24;

Console.WriteLine(matrix[4, 2]).

6.    Fonctions locales

Les fonctions locales permettent de déclarer des méthodes à l’intérieur d’une autre méthode. Il existe deux cas d’utilisation très courants pour les fonctions locales : les méthodes iterator publiques et les méthodes async publiques.

public static IEnumerable AlphabetSubset(char start, char end)

{

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;

}

}

public Task<string> PerformLongRunningWork(string address, int index, string 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.”;

}

}

7.    Autres membres expression-bodied

C# 6 a introduit les membres expression-bodied pour les fonctions membres, ainsi que des propriétés en lecture seule.

C#7 développe les membres autorisés pouvant être implémentés comme expressions. C’est-à-dire implémenter des constructeurs, des finaliseurs ainsi que des accesseurs get et set sur des propriétés et des indexeurs.

// 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”;

}

8.    Expressions throw

Cette version permet de lever des exceptions dans les constructions de code qui n’étaient pas autorisées auparavant, car throw était une instruction.

public string Name

{

get => name;

set => name = value ??

throw new ArgumentNullException(paramName: nameof(value), message: “New name must not be null”);

}

Cette fonctionnalité permet d’utiliser des expressions throw dans des expressions d’initialisation :

private ConfigResource loadedConfig = LoadConfigResourceOrDefault() ??

throw new InvalidOperationException(“Could not load config”);

Auparavant, ces initialisations devaient se trouver dans un constructeur, avec les instructions throw dans le corps du constructeur :

public ApplicationOptions()

{

loadedConfig = LoadConfigResourceOrDefault();

if (loadedConfig == null)

throw new InvalidOperationException(“Could not load config”);

}

9.    Types de retour async généralisés

Les méthodes déclarées avec le modificateur async peuvent retourner d’autres types en plus de Task et de Task<T>.

public ValueTask<int> CachedFunc()

{

return (cache) ? new ValueTask(cacheResult) : new ValueTask(LoadCache());

}

private bool cache = false;

private int cacheResult;

private async Task LoadCache()

{

// simulate async work:

await Task.Delay(100);

cacheResult = 100;

cache = true;

return cacheResult;

}

10.    Améliorations de la syntaxe littérale numérique

De nouveaux jetons améliorent la lisibilité des constantes numériques. Le séparateur de chiffres peut apparaître n’importe où dans la constante. Pour les nombres de base 10, il arrive fréquemment qu’il soit utilisé comme séparateur des milliers. Il est possible d’utiliser le séparateur de chiffres également avec les types decimal, float et double :

public const long BillionsAndBillions = 100_000_000_000;

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.

facebooktwittergoogle_plusmail

Laisser un commentaire