5 новинок в C#10

Многим интересно, в каком направлении будет идти развитие C# 10. Возможности не секрет. Стоит потратить немного времени на страницу C # в  GitHub, и вы найдете длинный список дразнящих идей, некоторые из которых еще не решены. Многие из них не войдут в следующую версию C #, а некоторые вообще не появятся в языке. (Существует высокая планка, чтобы попасть в C #, потому что язык требует, чтобы после поддержки ключевого слова или синтаксической структуры его поведение никогда не изменялось критически.)

Если вам интересно, какие функции гарантированно окажутся в новой версии языка, вы можете подождать, пока язык будет выпущен в ноябре вместе с .NET 6. Или вы можете следить людьми из команды C #, которые демонстрируют свои любимые функции. На прошлой неделе на конференции Microsoft Build ведущий разработчик C # Мэдс Торгерсен приоткрыл завесу с некоторых работ, которые в настоящее время ведутся. Вот пять новых функций, которые вы увидите в следующем выпуске языка.

1. Глобальные using

Обычно, файл исходного кода C# начинается с портянки перечислений используемых пространств имен. Вот фрагмент кода обычного файла веб приложения ASP .NET:

using LoggingTestApp.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;namespace LoggingTestApp
{
    public class Startup
    {
        ...
    }
}

Нет ничего необычного. Но когда-то давно импорт пространства имен дал нам быстрое представление о том, какие библиотеки использует класс. Сегодня это стало еще одним шаблоном, который мы обычно прокручиваем и не берем во внимание при изучении или написании кода.

В C# 10 представлен новый шаблон, который позволяет определять импорт пространства имен для всего проекта с помощью ключевого слова global. Рекомендуется помещать глобальный импорт в отдельный файл (по одному для каждого проекта), возможно, с именем usings.cs или imports.cs. Вот как это может выглядеть:

global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.HttpsPolicy;
global using Microsoft.AspNetCore.Identity;
global using Microsoft.AspNetCore.Identity.UI;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;

Теперь можно упростить файл с исходным кодом написав лишь одну строку using:

using LoggingTestApp.Data;
using Serilog;namespace LoggingTestApp
{
    public class Startup
    {
        ...
    }
}

Visual Studio выделяет дублирующиеся пространства имен (импортируемые глобально и локально в файл). Хотя это не ошибка, удаление повторяющихся пространств имен позволяет сократить объем стандартного кода и сосредоточить внимание на специальных пространствах имен, которые использует конкретный файл.

2. Пространства имен файловой области

Другой способ упростить ваш код на C# 10 — объявить для вашего кода пространство имен с файловой областью. Пространство имен с файловой областью автоматически применяется ко всему вашему файлу, без необходимости делать какие-либо отступы.

Другими словами, вы можете пойти от этого:

namespace LoggingTestApp
{
    public class Startup
    {
        ...
    }
}

К этому:

namespace LoggingTestApp;

public class Startup
{
    ...
}

Если вы добавляете блок пространства имен в файл, который использует пространство имен файловой области, он создает вложенное пространство имен, как и следовало ожидать:

namespace Company.Product;//Эта строчка создает пространство имен Company.Product.Component
namespace Component
{
}

Разработчики C # описывают это изменение как очистку горизонтальных отступов (как глобальное использование должно удалять вертикальные отступы). Общая цель — более короткое, более узкое и лаконичное выражение кода. Но эти изменения также являются частью продолжающейся работы, чтобы сделать C # менее устрашающим для новичков. Объедините глобальное использование и пространства имен с файловой областью, и вы сможете создать консольное приложение Hello World всего за несколько строк, что будет более простым началом, если вы только начали изучать язык.

3. Проверка нулевого параметра

В том же духе сокращения шаблонов, C # имеет очень приятную новую функцию, называемую проверкой нулевых параметров (null parametr checking). Несомненно, вы написали метод, который раньше должен был отклонять нулевые значения. Вероятно, вы использовали код, который выглядел так:

public UpdateAddress(int personId, Address newAddress)
{
    if (newAddress == null)
    {
        throw new ArgumentNullException("newAddress");
    }    ...
}

Теперь вы можете попросить C # автоматически вставить эту нулевую проверку, добавив !! в конец имени вашего параметра. В данном случае это newAddress:

public UpdateAddress(int personId, Address newAddress!!)
{
    ...
}

Теперь, если вы передадите нулевое значение вместо объекта Address, исключение ArgumentNullException будет сгенерировано автоматически.

Подобные детали могут показаться тривиальными, но на самом деле это очень ценный низко висящий плод оптимизации языка. Крупные исследования, изучающие причины катастрофических сбоев программирования, показывают, что одни и те же ошибки, которых легко избежать, повторяются снова и снова, не потому, что концепции в коде слишком сложны, а потому, что чтение кода утомительно, а у людей ограниченная продолжительность концентрации внимания. Уменьшение длины кода сокращает время, необходимое для его просмотра, когнитивную нагрузку, необходимую для его обработки, и вероятность того, что вы пропустите реальную ошибку, потому что ваше внимание притупилось.

4. Обязательные свойства

Раньше вы полагались только на конструкторы классов, чтобы убедиться, что объекты созданы в правильном состоянии. Сегодня мы часто работаем с более легкими конструкциями, такими как автоматически реализованные свойства в этой записи:

public record Employee
{
    public string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

И когда мы создаем экземпляры этих легких объектов, нам нравится делать это быстро с помощью синтаксиса инициализатора объекта:

var theNewGuy = new Employee
{
    Name = "Dave Bowman",
    YearlySalary = 100000m,
    HiredDate = DateTime.Now()
};

Но что, если ваш объект не имеет смысла, если не заданы определенные свойства? Вы можете добавить конструктор, как обычно, но вам нужно будет заплатить за эту формализацию, добавив еще несколько шаблонов. А копирование значений из параметров в свойства — еще один пример ошибки, которую легко понять, но которую часто делают.

В C# 10 ключевое слово required решает эту проблему:

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init; }
}

Теперь компилятор не позволит вам писать код, который создает Employee, но не устанавливает свойство Name.

5. Ключевое слово field

Команда C # за эти годы много сделала для оптимизации кода с помощью автореализуемых свойств. Показанная выше запись Employee является хорошим примером — она ​​объявляет три неизменяемых свойства с помощью ключевых слов get и init. Данные хранятся в трех частных полях, но эти поля создаются для вас автоматически и управляются без вашего вмешательства. Вы их никогда не увидите.

Автореализованные свойства — это здорово, но на них далеко не уйдешь. Когда они не подходят, вы вынуждены добавить поле поддержки в свой класс и написать обычные методы свойств, как будто вы вернулись в C # версии 2. Но в C# 10 есть новый бэкдор с ключевым словом field, которое предоставляет автоматически созданное резервное поле.

Например, предположим, что вы хотите создать запись, которая немного обрабатывает начальное значение свойства. Вот версия класса Employee, которая гарантирует, что поле HiredDate содержит только информацию о дате из объекта DateTime и не содержит информации о времени:

public record Employee
{
    public required string Name { get; init; }
    public decimal YearlySalary { get; init; }
    public DateTime HiredDate{ get; init => field = value.Date(); }
}

Этот код очистки красивый, простой и почти декларативный.

Вы можете использовать ключевое слово field для доступа к резервному полю в процедурах get, set или init. Итак, где вам могло понадобиться что-то подобное для проверки свойства в обычном классе:

private string _firstName;public string FirstName
{
    get
    {
        return _firstName;
    }
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");        _firstName = value;
    }
}

Теперь вы можете использовать автоматически реализованные свойство и поле:

public string FirstName {get;
    set
    {
        if (value.Trim() == "")
            throw new ArgumentException("No blank strings");        field = value;
    }
}

По сути, если вам не нужно изменять тип данных вашего ресурса, больше нет необходимости самостоятельно объявлять вспомогательное поле.

* * *

Конечно, эти 5 подтвержденных функций — далеко не все, что мы увидим в C# 10. Есть еще намеки на то, что мы получим больше работы с выражениями и спорное изменение, которое позволит нам определять статические члены в интерфейсе. Но нам нужно подождать, чтобы увидеть, в чем заключаются эти возможности.

Если вам интересно, как этот список соотносится с потенциальными функциями, о которых мы говорили ранее, история неоднозначна. Пространства имен с файловой областью видимости превратились из страны возможного в официальную территорию C# 10. Здесь нет первичных конструкторов, хотя они частично перекрываются с обязательными полями. А необработанные строковые литералы все еще остаются открытым вопросом. Но общая тема ясна: C# 10 продолжает развиваться, и его внимание сосредоточено на хорошо продуманных удобствах, которые лишь немного облегчают жизнь разработчикам.

Данный текст является переводом статьи Мэтью МакДональда на ресурсе Medium, с оригиналом статьи вы можете ознакомиться по ссылке.