Що нового буде в C# 9?

  • 6 февраля, 13:43
  • 5835
  • 0

Наступним релізом C# стане С# 9. У плані розвитку  вже описано 39 пропозицій (proposals) для дев'ятої версії. Але які пропозиції будуть впроваджені і в якій версії? 

Найбільш важливі функції, заплановані в C# 9, це новий тип - записи (records), диз'юнктні об'єднання (discriminated unions), поліпшення зіставлення зі зразком (pattern matching), додаткова цільова типізація існуючих конструкцій, таких як тернарні вирази і вирази об'єднання з null. 

Зазначені особливості ще не знаходяться на завершальній стадії. І остаточний синтаксис може відрізнятися від описаного в proposals.

Що нового буде в C# 9?

Записи

Записи (records) представляють собою нову спрощену форму оголошення класів C # і структурних типів. Опис цього нового типу дано на GitHub

Записи номінально типізовані і можуть мати методи, властивості, оператори і т.д., дозволяють проводити структурне порівняння. За замовчуванням властивості запису доступні тільки для читання. Записи можуть бути Value Type або Reference Type.

Запис може бути визначениц наступним чином:

public class Point3D   {     public int X { get; set; }     public int Y { get; set; }     public int Z { get; set; }   }

У разі незмінного типу пропонується використовувати новий модифікатор initonly, який можна застосовувати до властивостей і полів:  

public class Point3D   {   public initonly int X { get; } public initonly int Y { get; } public initonly int Z { get; } ... ...   }  

Створення об'єкта запису:

void DoSomething()   {     var point3D = new Point3D()     {       X = 1,       Y = 1,       Z =1     };   }  

Використання записів з with-виразами

У вступній частині заявки на впровадження записів в якості нового елемента пропонуються with-вирази. За допомогою with можна відразу змінювати копію об'єкта запису:

public class Demo   {     public void DoIt()     {       var point3D = new Point3D() { X = 1, Y = 1, Z =1  };       Console.WriteLine(point3D);     }   }    

var newPoint3D = point3D with {X = 42};  

Створена нова точка newPoint3D аналогічна існуючії point3D, але значення X змінено на 42.

Рівність

Записи порівнюються за структурою, а не за посиланням:

void DoSomething()   {       var point3D1 = new Point3D()        {           X = 1,           Y = 1,           Z =1       };       var point3D2= new Point3D()        {           X = 1,           Y = 1,           Z =1       };       var compareRecords = point3D1 == point3D2; // true   }  

Диз'юнктне об'єднання

Термін диз'юнктне об'єднання (discriminated union, disjoint union) запозичений з математики. Наприклад, є пов'язані безлічі A0 = {(5,0), (6,1)}і A1 = {(7,2)}. Диз'юнктне об'єднання полягає в об'єднанні «копій» множин, що не пересікаються:

A0 ⊔ A1 = {(5,0), (6,1), (7,2)}

Пропонована функціональність диз'юнктного об'єднання в C# 9 аналогічна F# і дозволяє визначати типи, які можуть містити будь-яку кількість різних типів даних, об'єднання корисні для різних неоднорідних даних і створення простих ієрархічних структур.

Приклад диз'юнктного об'єднання в F#:

type Person = {firstname:string; lastname:string}  // определяем запись   type ByteOrBool = Y of byte | B of bool   type MixedType =        | P of Person        // використаємо запис, введений вище      | U of ByteOrBool    // використаємо запис, введений вище  let unionRecord = MixedType.P({firstname="Bassam"; lastname= "Alugili"});    let unionType1 =  MixedType.U( B true);   // Boolean type   let unionType2 =  MixedType.U( Y 86uy);   // Byte type  

Візьмемо для диз'юнктного об'єднання в С# 9 пару сутностей:

// Знаходимо запис public class Person   {     public initonly string Firstname { get; }     public initonly string Lastname { get; }   };   enum class ByteOrBool { byte Y; bool B;} 

У другому випадку ми хочемо тип, який відображає всі можливі цілі числа і всі можливі булеві значення:

У нашому випадку новий тип ByteOrBool - це «сума» байтового і логічного типів. Як і в F#, сумарний тип називається discriminated union (диз'юнктне об'єднання). 

enum class MixedType   {     Person P;     ByteOrBool U;  

Створення екземпляра об'єднання:

var person = new Person()   {     Firstname = ”Bassam”;     Lastname = “Alugili”;   };   var unionRecord = new MixedType.P(person); // Record C# 9   var unionType1 = new MixedType.U( B true); // Boolean type   var unionType2 = new MixedType.U( Y 86uy); // Byte type  

Використання диз'юнктних об'єднань

Наведені нижче приклади демонстративні, тільки для кращого розуміння пропонованих нововведень.

1. Обробка винятків, як в Java:   

try   {         }   catch (CommunicationException | SystemException ex)   {      //Тут опрацьовуємо CommunicationException и SystemException 

2. Обмеження типу:

public class GenericClass<T> where T : T1 | T2 | T3 

Універсальний клас може належати одному з типів T1, T2 або T3.

3. Гетерогенні колекції:            

var crazyCollectionFP = new List<int|double|string>{1, 2.3, "bassam"};    

4. Комбінація змінних / значень / виразів різних типів через оператори ? :, ?? або вираз switch:      

var result = x switch { true => "Successful", false => 0 };  

Тип результату тут буде string | int.

5. Якщо кілька перевантажень будь-якого методу мають однакові реалізації, їх можна об'єднати. Наприклад, наступний набір

void logInput(int input) => Console.WriteLine($"The input is {input}");   void logInput(long input) => Console.WriteLine($"The input is {input}");   void logInput(float input) => Console.WriteLine($"The input is {input}");  

може бути замінений на єдиний рядок:

void logInput(int|long|float input) => Console.WriteLine($"The input is {input}");

6. Можна використовувати підхід для типів, що повертаються:        

public int|Exception Method() // возвращение исключения public class None {}   public typealias Option<T> = T | None; // Option type   public typealias Result<T> = T | Exception; // Result type  

Оператор об'єднання з null

Йдеться про дозвіл неявного перетворення для виразів, в яких відбувається об'єднання з null. Ось приклад в C# 8:

void M(List<int> list, uint? u)   {     IEnumerable<int> x = list ?? (IEnumerable<int>)new[] { 1, 2 }; // C# 8     var l = u ?? -1u; // C# 8   }  

У C# 9 той же код буде виглядати так:       

void M(List<int> list, uint? u)   {     IEnumerable<int> x = list ?? new[] { 1, 2 }; // C# 9     var l = u ?? -1; // C# 9   }  

Вираз new

Розглянемо приклад з офіційної пропозиції.

IEnumerable<KeyValuePair<string, string>> Headers = new[]   {     new KeyValuePair<string, string>("Foo", foo),     new KeyValuePair<string, string>("Bar", bar),   }  

Наведений код може бути спрощений до наступного 

IEnumerable<KeyValuePair<string, string>> Headers = new KeyValuePair<string, string>[]   {     new("Foo", foo),     new("Bar", bar),   }  

Але вам все одно потрібно повторно вказати тип після ініціалізації поля / властивості. Найближче, що можна отримати, це щось на зразок:

IEnumerable<KeyValuePair<string, string>> Headers = new[]   {     new KeyValuePair<string, string>("Foo", foo),     new("Bar", bar),   }  

Для повноти картини можна запропонувати також зробити new[] виразом з типом цільового об'єкта.

IEnumerable<KeyValuePair<string, string>> Headers = new[]   {     new("Foo", foo),     new("Bar", bar),   }

Атрибут виразу, що викликається

Ідея полягає в тому, щоб дозволити об'єкту, що викликається «структурувати» вирази, передані на місце виклику. Конструктор атрибута прийме строковий аргумент, який визначає ім'я аргументу для строкового перетворення.

public static class Verify {         public static void InRange(int argument, int low, int high,             [CallerArgumentExpression("argument")] string argumentExpression = null,             [CallerArgumentExpression("low")] string lowExpression = null,             [CallerArgumentExpression("high")] string highExpression = null) {             if (argument < low) {                 throw new ArgumentOutOfRangeException(paramName: argumentExpression, message: $ " {argumentExpression} ({argument}) cannot be less than {lowExpression} ({low}).");             }             if (argument > high) {                 throw new ArgumentOutOfRangeException(paramName: argumentExpression, message: $ "{argumentExpression} ({argument}) cannot be greater than {highExpression} ({high}).");             }         }         public static void NotNull < T > (T argument,             [CallerArgumentExpression("argument")] string argumentExpression = null)         where T: class {             if (argument == null) throw new ArgumentNullException(paramName: argumentExpression);         }     }     // CallerArgumentExpression: перетворює вираз в строку!       Verify.NotNull(array); // paramName: "масив"       // paramName: "индекс"       // Повідомлення про помилку через неправильний Index:        "index (-1) cannot be less than 0 (0).", or     // "index (6) cannot be greater than array.Length - 1 (5)."       Verify.InRange(index, 0, array.Length - 1);  

Спрощена default-деконструкція кортежу:

У C# 7 зіставляємо кожній змінній default:

(int x, string y) = (default, default);

У C# 9 простіше:       

(int x, string y) = default;

Вільний порядок модифікаторів ref і partial

При оголошенні класу можна вказувати partial перед ref

public ref partial struct {} // C# 7   public partial ref struct {} // C# 9  

Перевірка на null

Завдання - спростити стандартну null перевірку параметрів, використовуючи невелику анотацію параметрів. Ця функція відноситься до поліпшення якості коду. .  

//  в C# 1..7.x   void DoSomething(string txt)   {       if (txt is null)       {          throw new ArgumentNullException(nameof(txt));        }      }   // для C# 9   void DoSomething (string txt!)   {      }  

Порожні параметри для лямбда-виразів

Ідея полягає в тому, щоб дозволити при введенні лямбда-виразів множинні оголошення параметрів з ім'ям _. В цьому випадку параметри є «скинутими» (discard) і не можуть використовуватися всередині лямбди:   

Func zero = (_,_) => 0;   (_,_) => 1, (int, string) => 1, void local(int , int);  

Атрибути локальних функцій

Ідея полягає в тому, щоб дозволити атрибутам бути частиною оголошення локальної функції.

приклад:    

static void Main(string[] args)   {     static bool LocalFunc([NotNull] data)     {       return true;     }   }  

Ще один приклад використання - з EnumeratorCancellation для параметра CancellationToken локальної функції, що реалізує асинхронний итератор, що часто зустрічається при реалізації операторів запитів.

public static IAsyncEnumerable Where(this IAsyncEnumerable source, Func predicate)   {     if (source == null)         throw new ArgumentNullException(nameof(source));     if (predicate == null)         throw new ArgumentNullException(nameof(predicate));     return Core();     async IAsyncEnumerable<T> Core([EnumeratorCancellation] CancellationToken token = default)     {          await foreach (var item in source.WithCancellation(token))          {           if (predicate(item))           {               yield return item;           }          }     }   }  

Native Int

Пропонується ввести набір нативних типів (nint, nuint). Планується, що дизайн нових типів даних дозволить одному вихідному файлу C# використовувати 32- або 64-розрядні сховища в залежності від типу платформи хоста і налаштувань компіляції.

Тип визначається ОС:      

nint nativeInt = 55; // 4 байта при компиляції в 32 біт системі  nint nativeInt = 55; // 8 байт при компиляції  в 64 біт системі з x64 налаштуваннями

Покажчики на функції

Термін «покажчик на функцію» багатьом відомий з С/С++. В зміннії зберігається адреса функції, яка згодом може бути викликана через покажчик цієї функції. Покажчики на функції можна викликати і передавати їм аргументи, як при звичайному виконанні функції. 

Покажчик на функцію C# дозволяє оголошувати покажчики з використанням синтаксису func*. Це схоже на синтаксис, який використовується при оголошенні делегатів:

unsafe class Example   {     void Example(Action<int> a, delegate*<int, void> f)     {       a(42);       f(42);     }   }  

Висновок

Отже, ви прочитали про ймовірні зміни в C # 9. Багато що ще обговорюється, запропоновані функції і синтаксис/ емантика можуть бути змінені.

Джерело перекладу 


0 комментариев
Сортировка:
Добавить комментарий

IT Новости

Смотреть все