Полное руководство по языку программирования С# 14 и платформе .NET 10 / Что нового

C# и .NET | Нововведения в C# 14

Начальный ⏱ 6 мин чтения 👁 2 🕓 обновлено 11.08.2026

Специальное поле field

Специальное поле field, которое генерируется компилятором, позволяет обращаться внутри свойства к полю, которое хранит значение для данного свойства. Это избавляет от необходимости явного создания поля для свойства. Например, если в предыдущих версиях C# для обращения к полю нам его надо было явно определять:


class Person
{
    string? name;
 
    public string? Name
    {
        get
        {
            return name;    // возвращаем значение свойства
        }
        set
        {
            name = value;   // устанавливаем новое значение свойства
        }
    }
}

То начиная с версии C# 14 мы можем вместо него использовать field:


class Person
{
    // string? name;  это поле больше не нужно
 
    public string? Name
    {
        get
        {
            return field;    // возвращаем значение свойства
        }
        set
        {
            field = value;   // устанавливаем новое значение свойства
        }
    }
}

Существует потенциальная возможность критических изменений или путаницы в типах, которые также содержат компонент (поле/переменную) с именем field, особенно при миграции проектов на C# 14 с более ранних версий. В этом случае можно использовать @field или this.field для устранения неоднозначности между ключевым словом field и идентификатором, или всегда можно переименовать компонент field для более четкого различия.

Определение составных операторов присваивания

Начиная с версии C# 14 можно для своих типов настраивать поведение составных операторов присваивания таким образом, чтобы цель присваивания изменялась на месте. При этом язык C# уже в более ранних версиях позволял определять обычный оператор (без присваивания), который также можно использовать в качестве составного оператора с присваиванием. Например:


Counter counter1 = new Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

class Counter
{
    public int Value { get; set; } 
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}

Таким образом, хотя мы явным образом НЕ определяли составной оператор присваивания += для типа Counter, но благодаря оператору сложения + мы можем выполнять сложение с присвоением.

Однако, инструкция

counter1 += 45;

Фактически аналогична следующей:

counter1 = counter1 + 45;

То есть при сложении будет создаваться новый объект Counter. И чтобы решить подобную проблему в версию языка C# 14 (.NET 10) была добавлена возможность отдельно определеить составные операции присваивания, которы вместо создания нового объекта позволяют изменить текущий объект. Фактически, составной оператор присваивания - это метод объекта, который возвращает void (то есть фактически ничего не возвращает), который принимает один параметр и который имеет специальное имя название оператора:


Counter counter1 = new Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

class Counter
{
    public int Value { get; set; }

    public void operator +=(int n)
    {
        Value += n;
    }
}

Подробнее про определение составных операторов присваивания в соответствующей статье.

Блоки расширений

Начиная с версии C# 14 можно использовать новый синтаксис для определения членов расширения - с помощью блоков extension, которые далее будет именовать блоками расширений

Блоки расширения позволяют добавить новые свойства, методы, операторы к уже существующим типам. С помощью блоков расширения можно определять два типа расширений:

  • Расширения экземпляров: расширяют экземпляр типа

  • Статические расширения: расширяют сам тип

Пример добавления свойств, методов и операторов к встроенному типу String:


string s = "Привет мир";
Console.WriteLine(s.CharCount('и'));   // 2
Console.WriteLine("123".IsNumber);   // true
Console.WriteLine("abc".IsNumber);   // false

Console.WriteLine("hello" > "ello");  // true
Console.WriteLine("hello" < "ello");  // false

Console.WriteLine(string.Default);   // Hello World

static class StringExtension
{
    // нестатический блок расширений
    extension(string str)
    {
        public bool IsNumber
        {
            get
            {
                for (int i = 0; i < str.Length; i++)
                {
                    if (str[i] < '0' || str[i] > '9') return false;
                }
                return true;
            }
        }
        public int CharCount(char c)
        {
            int counter = 0;
            for (int i = 0; i < str.Length; i++)
            {
                if (str[i] == c) counter++;
            }
            return counter;
        }
    }
    // статический блок расширений
    extension(string)
    {
        // операторы
        public static bool operator >(string str1, string str2) => str1.Length > str2.Length;
        public static bool operator <(string str1, string str2) => str1.Length < str2.Length;

        // статическое свойство
        public static string Default => "Hello World";
    }
}

Подробнее про блоки расширений в соответствующей статье.

Параметры лямбда-выражения с модификаторами

До C# 14 к параметрам лямбда-выражения можно было добавлять модификаторы (такие как scoped, ref, in, out или ref readonly) только при условии, что объявления параметров включали типы параметров. Начиная с C# 14 модификаторы можно добавлять без указания типа параметра. Например, раньше мы писали:


var changeAge = (ref int age, int new_value) => {if(new_value > 0) age = new_value;};

То начиная с C# 14 мы можем для параметров не указывать тип:


delegate void ChangeInt(ref int old_value, int new_value);

ChangeInt changeAge = (ref age, new_value) => {if(new_value > 0) age = new_value;};

Единственный минус - в этом случае может потребоваться объявление делегата под такое лямбда-выражение

Пример применения:


ChangeInt changeAge = (ref age, new_value) => {if(new_value > 0) age = new_value;};

int userAge = 34;
changeAge(ref userAge, 41);
Console.WriteLine(userAge); // 41

delegate void ChangeInt(ref int old_value, int new_value);

Присваивание с проверкой на null

Операторы доступа с проверкой на null - ?. и ?[] теперь можно использовать в левой части присваивания или составного присваивания. До C# 14 перед присваиванием свойству требовалось проверять переменную на null:


Person? person = null;

if(person is not null)
{
    person.Name = "Tom";
}

Console.WriteLine(person?.Name);   // ничего не выводит 

class Person
{
    public string? Name {get; set;} 
}

Начиная с C# 14 предыдущий код можно упростить с помощью оператора ?.:


Person? person = null;

// присваивание, если только person не равно null
person?.Name = "Tom";

Console.WriteLine(person?.Name);   // ничего не выводит 

class Person
{
    public string? Name {get; set;} 
}

В выражении person?.Name = "Tom" правая часть оператора присвоения = вычисляется только тогда, когда левая часть не равна null. Если переменная person равна null, присвоения не будет.

Причем та же логика присваивания с проверкой на null применяется и с составными операторами присваивания (+=, -= и другими). ​​Однако инкремент и декремент (++ и --) не допускаются.

Приложения на основе файлов

Начиная с C# 14 и .NET 10 можно создавать приложения на основе файлов (file-based app). Это упрощает сборку и запуск программ на C#.

Теперь для создания и запуска программы на C# достаточно определить файл с кодом на C# и передать его команде команды dotnet run. Например, определим файл hello.cs с каким-нибудь простеньким содержимым:

Console.WriteLine("Hello METANIT.COM");

В консоли перейдем в папке, где располагается данный файл, и выполним команду dotnet run hello.cs


eugene@Eugene:/dotnet/scripts$ dotnet run hello.cs
Hello METANIT.COM
eugene@Eugene:/dotnet/scripts$ 

Проверь себя

📋 Тест по теме «C# и .NET | Нововведения в C# 14» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

class _____
{
    string? name;
 
    public string? Name
    {
        get
        {
            return name;    // возвращаем значение свойства
        }
        set
        {
            name = value;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

class Person
{
    // string? _____;  это поле больше не нужно
 
    public string? Name
    {
        get
        {
            return field;    // возвращаем значение свойства
        }
        set
        {
            field = value;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Counter counter1 = _____ Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

class Counter
{
    public int Value { get; set; } 
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

counter1 += _____;
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____ = counter1 + 45;
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

class Person
{
    string? _____;
 
    public string? _____
    {
        get
        {
            return name;    // возвращаем значение свойства
        }
        set
        {
            name = value;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

class Person
{
    // string? name;  это поле больше не нужно
 
    public string? Name
    {
        get
        {
            return _____;    // возвращаем значение свойства
        }
        set
        {
            field = _____;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

delegate void ChangeInt(ref int old_value, int new_value);
int userAge = 34;
changeAge(ref userAge, 41);
Console.WriteLine(userAge); // 41
ChangeInt changeAge = (ref age, new_value) => {if(new_value > 0) age = new_value;};
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «C# и .NET | Нововведения в C# 14»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «C# и .NET | Нововведения в C# 14», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

class Person
{
    string? name;
 
    public string? Name
    {
        get
        {
            return name;    // возвращаем значение свойства
        }
        set
        {
            name = value;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «C# и .NET | Нововведения в C# 14»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

class Person
{
    // string? name;  это поле больше не нужно
 
    public string? Name
    {
        get
        {
            return field;    // возвращаем значение свойства
        }
        set
        {
            field = value;   // устанавливаем новое значение свойства
        }
    }
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «C# и .NET | Нововведения в C# 14»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «C# и .NET | Нововведения в C# 14»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «C# и .NET | Нововведения в C# 14». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.