СПРАВОЧНИК
Назначение
Справочник-шпаргалка по C# — типы, синтаксис, стандартная библиотека, типовые паттерны. Не заменяет пошаговое обучение. Учебный курс: раздел.
Полный официальный каталог: справочник языка на Learn (869 разделов) · справочник .NET API (BCL).
Краткое пояснение
Компоненты и ключевые особенности языка.
Полная карта C# Language Reference (Microsoft Learn)
Ниже — маршрут по официальному справочнику языка C# "по максимуму" — ключевые слова, операторы/выражения, инструкции языка, параметры компилятора, сообщения компилятора и спецификация.
Ядро справочника
Ключевые слова, операторы и инструкции
Компилятор, документация, спецификация
Полный API .NET
Справочные таблицы
Лексика, типы, операторы, ключевые слова
1. Типы данных
Встроенные (примитивные) типы и их псевдонимы
| C# псевдоним | Системный тип | Размер (бит) | Диапазон / Примечание |
|---|
bool | System.Boolean | 8 | true / false |
sbyte | System.SByte | 8 | −128 … 127 |
byte | System.Byte | 8 | 0 … 255 |
short | System.Int16 | 16 | −32 768 … 32 767 |
ushort | System.UInt16 | 16 | 0 … 65 535 |
int | System.Int32 | 32 | −2 147 483 648 … 2 147 483 647 |
uint | System.UInt32 | 32 | 0 … 4 294 967 295 |
long | System.Int64 | 64 | −2⁶³ … 2⁶³−1 |
ulong | System.UInt64 | 64 | 0 … 2⁶⁴−1 |
nint | System.IntPtr | 32/64 | зависит от архитектуры (указательный размер) |
nuint | System.UIntPtr | 32/64 | зависит от архитектуры |
char | System.Char | 16 | UTF-16 символ (один элемент кодировки) |
float | System.Single | 32 | ~±1.5 × 10⁻⁴⁵ … ±3.4 × 10³⁸, 7 цифр точности |
double | System.Double | 64 | ~±5.0 × 10⁻³²⁴ … ±1.7 × 10³⁰⁸, 15–16 цифр |
decimal | System.Decimal | 128 | ±1.0 × 10⁻²⁸ … ±7.9 × 10²⁸, 28–29 цифр, для финансовых вычислений |
string | System.String | ref | ссылка на неизменяемую UTF-16 строку |
object | System.Object | ref | корневой тип всех ссылочных и упакованных значений |
Категории типов
- Значимые (value types) — структуры (
struct), перечисления (enum), простые типы (int, DateTime, Guid, Span<T>, UnsafePointer, nint).
- Ссылочные (reference types) — классы (
class), интерфейсы (interface), делегаты (delegate), массивы, строки — все наследуются от System.Object.
- Указатели (
T*): доступны только в unsafe-контексте.
Специальные типы / конструкции
| Конструкция | Описание |
|---|
dynamic | Отложенное связывание: проверка членов происходит во время выполнения. Наследуется от System.Object, но компилятор отключает статическую проверку. |
var | Указание компилятору вывести тип на основе инициализатора. Работает только с локальными переменными. |
record | Синтаксический сахар для неизменяемых (по умолчанию) типов с генерацией Equals, GetHashCode, ToString, операторов ==/!=, with и (в record struct) copy-конструкторов. |
record struct | Значимый record, появился в C# 10. |
ref struct | Значимый тип, который не может покидать стек: не может быть полем класса, элементом массива, кэширован в async, упакован в object. Примеры: Span<T>, ReadOnlySpan<T>. |
required (C# 11) | Модификатор свойства или поля в record/class/struct, требующий обязательной инициализации либо в инициализаторе объекта, либо в конструкторе. |
init | Модификатор метода-сеттера: допускает присваивание только во время инициализации объекта (в конструкторе или инициализаторе). |
readonly (для struct) | Гарантирует, что экземпляр структуры не будет изменён после инициализации — все поля readonly, методы readonly. |
readonly ref | Возвращаемое значение ссылки на readonly-данные — запрещает их модификацию. |
scoped (C# 12) | Указывает, что ссылка (в т.ч. ref, Span<T>) не должна выходить за пределы текущего блока/метода. |
2. Ключевые слова языка
Обязательные (не могут быть идентификаторами)
abstract, as, base, bool, break, byte, case, catch, char, checked, class, const, continue, decimal, default, delegate, do, double, else, enum, event, explicit, extern, false, finally, fixed, float, for, foreach, goto, if, implicit, in, int, interface, internal, is, lock, long, namespace, new, null, object, operator, out, override, params, private, protected, public, readonly, ref, return, sbyte, sealed, short, sizeof, stackalloc, static, string, struct, switch, this, throw, true, try, typeof, uint, ulong, unchecked, unsafe, ushort, using, virtual, void, volatile, while
Контекстные ключевые слова (могут быть идентификаторами в других контекстах)
add, alias, and, ascending, async, await, by, descending, dynamic, equals, from, get, global, group, init, into, join, let, nameof, nint, not, nuint, on, or, orderby, partial, record, remove, required, select, set, unmanaged, value, var, when, where, with, yield
Примечание: global используется в global::System.Int32 для разрешения коллизий имён с помощью глобального пространства имён.
3. Модификаторы доступа
| Модификатор | Применяется к | Видимость |
|---|
public | Любой член | Везде |
protected | Член класса | Внутри содержащего класса и производных классов |
internal | Любой член | В пределах сборки (assembly) |
protected internal | Член класса | Внутри сборки или в производных классах (в любой сборке) |
private | Любой член | Только внутри содержащего типа |
private protected | Член класса | Только в производных классах внутри той же сборки |
Уровень по умолчанию:
- Для
class/struct: internal
- Для членов:
private
4. Модификаторы членов (не доступа)
| Модификатор | Применение | Эффект |
|---|
static | класс, метод, поле, свойство, событие, конструктор, оператор | Принадлежит типу, а не экземпляру |
readonly | поле, struct | Поле инициализируется только в объявлении или конструкторе; readonly struct — все поля readonly, все методы readonly |
const | поле | Компилируемая константа (только для bool, целых, char, string, enum). Подставляется в IL как литерал. |
virtual | метод, свойство, событие, индексатор | Разрешает переопределение в производном классе |
override | метод, свойство, событие, индексатор | Явное переопределение virtual/abstract члена |
abstract | класс, метод, свойство, событие, индексатор | Тип или член без реализации — должен быть переопределён в неабстрактном классе |
sealed | класс, метод (override), record | Класс — запрет на наследование; метод — запрет дальнейшего переопределения |
partial | class, struct, interface, метод | Разделение определения на несколько файлов или частей (требуется одинаковое имя и сигнатура) |
extern | метод | Реализация вынесена за пределы кода (P/Invoke, DllImport) |
unsafe | блок, метод, тип | Включение режима указателей и небезопасных операций |
volatile | поле | Запрещает компилятору и CPU применять оптимизации, нарушающие порядок чтения/записи (для многопоточной синхронизации без lock) |
async | метод, локальная функция | Указывает, что метод может содержать await и возвращает Task/Task<T>/ValueTask/ValueTask<T>/IAsyncEnumerable<T>/void (только для event handlers) |
ref | параметр, возвращаемое значение, локальная переменная, struct | Передача/возврат по ссылке (не копируется значение) |
in | параметр | ref readonly — передача по ссылке без возможности модификации |
out | параметр | Выходной параметр: инициализация обязательна в методе, не читается до присвоения |
params | последний параметр массива | Позволяет вызывать метод с переменным числом аргументов (например, params int[] values) |
required | свойство, поле (в C# 11+) | Обязательная инициализация при создании объекта |
scoped | параметр (ref, Span<T> и др.) | Гарантия, что ссылка не покинет текущую область |
5. Операторы
Приоритет (уменьшается сверху вниз)
| Приоритет | Операторы | Примечания |
|---|
| 20 | x.y, x?.y, f(x), a[x], x++, x--, new, typeof, checked, unchecked, default, nameof, sizeof, stackalloc, x->y, x! (null-forgiving), x is T, x as T | Постфиксные, вызовы, доступ по индексу, создание |
| 19 | +x, -x, !x, ~x, ++x, --x, ^x (index from end), await, &x, *x, (T)x | Унарные, приведения, указательные операции |
| 18 | x * y, x / y, x % y | Мультипликативные |
| 17 | x + y, x - y | Аддитивные |
| 16 | x << y, x >> y | Побитовые сдвиги |
| 15 | x < y, x <= y, x > y, x >= y, is, as | Сравнение, проверка типа |
| 14 | x == y, x != y | Равенство |
| 13 | x & y | Побитовое И (для bool — несокращённое логическое И) |
| 12 | x ^ y | Побитовое исключающее ИЛИ (XOR) |
| 11 | x | y | Побитовое ИЛИ (для bool — несокращённое логическое ИЛИ) |
| 10 | x && y | Сокращённое логическое И |
| 9 | x || y | Сокращённое логическое ИЛИ |
| 8 | x ?? y | Null-coalescing |
| 7 | c ? a : b | Тернарный условный |
| 6 | x = y, x op= y, x ??= y, x ?= y (нет), => — не оператор | Присваивание (все возвращают присвоенное значение) |
| 5 | yield return, yield break | Только в итераторах (IEnumerable<T>, IAsyncEnumerable<T>) |
| 4 | => | Лямбда-выражение (не входит в приоритет таблицы как оператор) |
Специальные операторы и конструкции
| Оператор / Конструкция | Описание |
|---|
is | Проверка типа, сопоставление с образцом: obj is string s, x is > 0 and < 10, x is int[] { Length: 5 } |
as | Безопасное приведение: возвращает null, если несовместимо (только для ссылочных типов и Nullable<T>) |
?? | Null-coalescing: a ?? b → если a null, то b, иначе a |
??= | Null-coalescing assignment: a ??= b ⇔ a = a ?? b |
?. / ?[] | Условный оператор доступа: obj?.Prop?[0]?.Method() — прерывает цепочку при null |
! | Null-forgiving (suppression) operator — отключает предупреждения NRT (nullable reference types) |
^ | Index from end: arr[^1] — последний элемент; ^n = Length - n (требуется using System;) |
.. | Range operator: arr[1..^1] — срез от индекса 1 до предпоследнего; ..3, 2.., .. |
() | Вызов, группировка, приведение, кортеж |
[] | Доступ по индексу, объявление массива, атрибуты |
{} | Инициализатор объекта, коллекции, анонимного типа, блок кода |
=> | Expression-bodied member, лямбда |
nameof(...) | Получение имени идентификатора как string (без hardcode) — работает на этапе компиляции |
default(T) / default | Значение по умолчанию для типа: 0, false, null, обнуленная структура |
sizeof(T) | Размер типа в байтах — только для неуправляемых типов и в unsafe или Unsafe.SizeOf<T>() в безопасном коде |
stackalloc T[n] | Выделение массива на стеке (только в unsafe или для Span<T> в безопасном коде с ReadOnlySpan<byte> инициализаторами) |
&x, *p | Адрес и разыменование — только в unsafe |
Члены типов
1. Поля (Fields)
Синтаксис
[<атрибуты>] [<модификаторы>] <тип> <имя> [= <инициализатор>];
Типы полей
| Тип | Пример | Примечание |
|---|
| Обычное | private int _count; | Хранит состояние экземпляра |
| Статическое | public static readonly DateTime Epoch = new(1970, 1, 1); | Принадлежит типу |
readonly | public readonly Guid Id = Guid.NewGuid(); | Инициализируется только в объявлении или конструкторе |
const | public const double Pi = 3.141592653589793; | Компилируемая константа (только для примитивов и string) |
volatile | private volatile bool _isCancelled; | Запрещает оптимизации, нарушающие порядок чтения/записи |
required (C# 11) | public required string Name; | Обязательное к инициализации при создании объекта (new X { Name = "..." } или конструктор) |
init (через field в C# 12) | public string Name { get; init; } = field ?? ""; — field ссылается на неявное поле | Только в свойствах с init или get/set |
ref struct поле | Запрещено — ref struct не может быть полем обычного класса/структуры | |
ref поле | Нельзя объявить напрямую, но можно через unsafe-указатель или ref-параметр/локальную переменную | |
fixed (буфер) | public unsafe fixed byte Buffer[256]; | Только в unsafe, только в struct, выделяется инлайн |
Инициализация
- Порядок инициализации:
- Статические поля → статический конструктор
- Поля экземпляра → конструктор базового класса (
base(...)) → конструктор текущего класса
required поля не инициализируются автоматически — контроль на этапе компиляции (если не указано при создании, ошибка CS9035).
2. Свойства (Properties)
Синтаксис
[<атрибуты>] [<модификаторы>] <тип> <Имя>
{
[<модификаторы>] get { <тело> }
[<модификаторы>] set { <тело> }
[<модификаторы>] init { <тело> }
}
public string Name => _name;
public int Length => _items?.Length ?? 0;
Виды свойств
| Тип | Пример | Примечание |
|---|
| Автосвойство | public string Name { get; set; } | Компилятор генерирует скрытое поле <Name>k__BackingField |
init-свойство | public string Id { get; init; } | Присваивание только при инициализации (new X { Id = "..." }, первичный конструктор, with) |
required-свойство | public required string Login { get; set; } | Обязательное заполнение при создании — иначе ошибка компиляции |
readonly автосвойство | public string Version { get; } = "1.0"; | Только get; инициализация — в конструкторе или при объявлении |
virtual/abstract/override | public virtual int Count => _items.Count; | Поддерживает переопределение |
| Выражение | public bool IsEmpty => Count == 0; | Краткая форма для get |
| Полное (с логикой) | private string _name; public string Name { get => _name; set => _name = value?.Trim() ?? ""; } | Контроль доступа и валидации |
| Индексатор | public T this[int index] => _items[index]; | Свойство с параметром — имя всегда this |
Ключевые слова в set/init
value — неявный параметр, тип совпадает с типом свойства.
field (C# 12) — ссылка на неявное поле автосвойства (только в get/set/init, не в обычных свойствах с явным полем):
public string Name
{
get => field?.ToUpper();
init => field = value?.Trim() ?? "";
}
3. События (Events)
Синтаксис
[<атрибуты>] [<модификаторы>] event <делегат> <Имя>;
private EventHandler<MyEventArgs> _myEvent;
public event EventHandler<MyEventArgs> MyEvent
{
add => _myEvent += value;
remove => _myEvent -= value;
}
Стандартные делегаты
| Делегат | Сигнатура | Применение |
|---|
EventHandler | void Method(object sender, EventArgs e) | Без данных события |
EventHandler<T> | void Method(object sender, T e) where T : EventArgs | С данными (T наследует EventArgs) |
| Пользовательский | delegate void MyDelegate(string msg); | Редко — предпочтительно Action<T>/Func<T,R> или EventHandler<T> |
Правила
- Внутри класса событие вызывается как делегат:
MyEvent?.Invoke(this, args);
- За пределами — только
+= и -= (компилятор запрещает прямой вызов и присваивание).
event-поле нельзя явно инициализировать (= null избыточно — по умолчанию null).
- В многопоточной среде рекомендуется копировать делегат перед вызовом:
var handler = MyEvent;
handler?.Invoke(this, args);
Или использовать Interlocked.CompareExchange / Volatile.Read.
4. Методы (Methods)
Синтаксис
[<атрибуты>] [<модификаторы>] <возвращаемый тип> <Имя>([<параметры>])
{
<тело>
}
public int Square(int x) => x * x;
Параметры
| Способ передачи | Синтаксис | Поведение |
|---|
| По значению (по умолчанию) | void M(int x) | Копия значения (для ссылочных — копия ссылки) |
ref | void M(ref int x) | Передача по ссылке — изменения влияют на аргумент |
out | bool TryParse(string s, out int result) | Выходной параметр — должен быть проинициализирован в методе |
in | void M(in ReadOnlySpan<byte> data) | ref readonly — нельзя модифицировать, но избегается копирование |
params | void Log(params string[] messages) | Последний параметр массива; вызов: Log("a", "b", "c") |
| По умолчанию | void M(int x = 10) | Значение подставляется, если аргумент не указан |
| Именованный | M(x: 5, y: "test") | Порядок не важен; сочетается с params (Log(severity: Info, messages: "a", "b")) |
Особенности методов
| Вид | Пример | Примечание |
|---|
| Статический | public static int Max(int a, int b) => Math.Max(a, b); | Нет this |
| Виртуальный | public virtual void Draw() { ... } | Может быть переопределён |
| Абстрактный | public abstract void Execute(); | Только в abstract-классе, без тела |
| Переопределённый | public override string ToString() => $"Point({X}, {Y})"; | Должен соответствовать сигнатуре базового virtual/abstract |
| Запечатанный | public sealed override void Draw() { ... } | Запрещает дальнейшее переопределение |
| Частичный | partial void Validate(); | Может быть определён в другой части partial class; если не реализован — компилятор удаляет вызовы |
| Внешний | public static extern int GetTickCount(); [DllImport("kernel32")] ... | Реализация в нативном коде |
| Асинхронный | public async Task<int> DownloadAsync() | Должен содержать await или возвращать Task/ValueTask/IAsyncEnumerable |
| Итераторный | public IEnumerable<int> Range(int start, int count) → yield return i; | Компилятор генерирует IEnumerator<T> |
| Локальная функция | void Outer() { int Helper() => 42; } | Доступна только в методе; может быть static, async, iterator |
| Обобщённый | public T FirstOrDefault<T>(IEnumerable<T> source, Func<T, bool> pred) | Параметры типа: where T : class, struct, new(), IDisposable, notnull, unmanaged |
Generic constraints (ограничения параметров типа)
| Ограничение | Пример | Значение |
|---|
where T : class | class Box<T> where T : class | Ссылочный тип (включая string, делегаты, массивы) |
where T : class? | (C# 9+, NRT) | Ссылочный тип, допускает null |
where T : struct | Span<T> where T : struct | Неуправляемый значимый тип (не Nullable<T>) |
where T : unmanaged | (C# 7.3) | Значимый тип без ссылок и GC-трекинга — можно использовать в unsafe |
where T : new() | T Create<T>() where T : new() => new T(); | Должен иметь публичный конструктор без параметров |
where T : IDisposable | void Use<T>(T obj) where T : IDisposable | Должен реализовывать интерфейс |
where T : notnull | (C# 8+) | Запрещает null даже в nullable-контексте |
where T : U | void Copy<T, U>(T src, U dst) where T : U | T должен быть U или производным от U |
5. Конструкторы (Constructors)
Виды
| Тип | Синтаксис | Примечание |
|---|
| Экземпляра | public Point(int x, int y) { X = x; Y = y; } | Вызывается при new |
| Статический | static MyClass() { ... } | Вызывается один раз перед первым использованием типа |
| Первичный (C# 12) | public record Person(string Name, int Age) { ... } | Сокращённая форма для record и class/struct — параметры становятся public readonly полями/свойствами |
| Делегирование | public Point() : this(0, 0) { } | Цепочка через this(...) или base(...) |
Поведение
- Если нет конструктора экземпляра — компилятор генерирует публичный без параметров (
public C() — base() {}), кроме record, где генерируется первичный (если указаны параметры) или без параметров.
record с первичным конструктором автоматически создаёт:
- Поля/свойства для параметров (по умолчанию
public init для record class, public readonly для record struct)
Deconstruct
ToString, Equals, GetHashCode, ==, !=, оператор with
struct всегда имеет неявный конструктор без параметров (нельзя переопределить), но можно определить свой — при этом поля должны быть инициализированы до выхода.
6. Финализаторы (Finalizers)
- Компилируется в
protected override void Finalize().
- Вызывается GC асинхронно, не гарантируется порядок и момент.
- Не использовать для управляемых ресурсов — только для
IntPtr, хэндлов ОС.
- Рекомендуется использовать
IDisposable + SafeHandle вместо финализатора.
- Если реализуете
IDisposable, финализатор должен вызывать Dispose(false).
7. Операторы (Operators)
Перегружаемые
| Оператор | Сигнатура | Примечание |
|---|
| Унарные | public static T operator +(T a) | +, -, !, ~, ++, --, true, false |
| Бинарные | public static T operator +(T a, T b) | +, -, *, /, %, &, |, ^, <<, >>, ==, !=, <, >, <=, >= |
| Приведения | public static explicit operator T(U u) public static implicit operator T(U u) | explicit — требует явного приведения; implicit — не требует |
Правила
== и != должны быть перегружены парно.
- Если перегружены
==/!=, то рекомендуется переопределить Equals и GetHashCode.
true/false — нужны для &&/\|\| (компилятор преобразует a && b → a ? b : false при наличии true/false).
implicit приведение должно быть безопасным (без потерь, исключений).
- Нельзя перегрузить —
=, &&, \|\|, ??, ?., ->, ?:, new, typeof, sizeof, is, as, checked, unchecked.
8. Вложенные типы
public class Outer
{
private int _x;
public class Nested { }
public struct NestedStruct { }
public interface INested { }
public delegate void NestedDelegate();
public enum NestedEnum { A, B }
}
- Доступность по умолчанию:
private.
- Вложенные типы наследуют generic-параметры внешнего типа:
class Outer<T>
{
class Inner : IEnumerable<T> { ... }
}
private protected вложенный тип виден только в производных классах внутри той же сборки.
9. Частичные (partial) методы
partial void OnValidate();
partial void OnValidate()
{
}
- Должны возвращать
void.
- Могут иметь
out-параметры, но не ref/in/params.
- Могут быть
static, но не virtual, abstract, override.
- Используются в source generators (например, для hook-ов инициализации).
10. Записи (record, record struct)
| Фича | record class | record struct |
|---|
| Неизменяемость по умолчанию | init-свойства | readonly поля (если нет set) |
with-выражение | Да (var y = x with { Name = "new" };) | Да (копирует все поля) |
| Первичный конструктор | record R(string Name); → public string Name { get; init; } | То же, но поля readonly |
Deconstruct | Автогенерируется: void Deconstruct(out string Name) | Автогенерируется |
Equals/GetHashCode | Сравнение по значению полей/свойств | То же |
==/!= | Перегружены | Перегружены |
| Наследование | Да (но только от record или object) | Нет (структуры не наследуются) |
PrintMembers | protected virtual bool PrintMembers(StringBuilder sb) | Нет (но можно определить) |
Управление памятью и производительность
1. Упаковка (boxing) и распаковка (unboxing)
Что происходит
- Boxing — преобразование значимого типа в
object или интерфейс → выделение памяти в куче, копирование значения, возврат ссылки.
int x = 42;
object boxed = x;
- Unboxing: извлечение значения из упакованного объекта → проверка типа (
isinst), копирование значения.
Стоимость
| Операция | Стоимость | Примечание |
|---|
| Boxing | ~100–300 тактов | Выделение памяти, вызов GC.Alloc, копирование |
| Unboxing | ~10–30 тактов | Проверка типа (RTTI), копирование |
| Повторное boxing одного и того же значения | Не кэшируется (кроме случаев ниже) | object a = 5; object b = 5; ReferenceEquals(a, b) → false |
Кэширование значений (CLR-оптимизация)
Только для:
bool: true, false
char: U+0000 … U+007F (0–127)
- Целые (
sbyte, byte, short, ushort, int): значения от –128 до 127
object a = 100, b = 100;
Console.WriteLine(ReferenceEquals(a, b));
object c = 300, d = 300;
Console.WriteLine(ReferenceEquals(c, d));
Как избежать
- Использовать обобщённые коллекции (
List<int>, а не ArrayList).
- Избегать
object/IComparable/IEquatable<object> для значимых типов.
- Применять
Span<T>, ref, in, ref struct.
- В интерфейсах использовать generic-ограничения:
where T : IEquatable<T>, а не IEquatable<object>.
2. IDisposable, IAsyncDisposable и управление ресурсами
Стандартный шаблон (Dispose pattern)
Код ITЗагрузка примера кода…
Ключевые правила
| Правило | Примечание |
|---|
Dispose() должен быть идемпотентным | Повторные вызовы — безопасны |
GC.SuppressFinalize(this) — обязательно в Dispose() | Иначе объект попадёт в финализационную очередь, замедляя GC |
IAsyncDisposable должен возвращать ValueTask, а не Task | Для избежания аллокаций |
await using → вызывает DisposeAsync() | Аналог using, но для асинхронного освобождения |
using var x = ...; — синтаксический сахар | Блок using до конца текущей области видимости |
Когда НЕ нужен финализатор
- Если ресурсы обёрнуты в
SafeHandle (рекомендуется).
- Если используются только управляемые ресурсы (
Stream, HttpClient, SqlConnection и т.п.).
SafeHandle — предпочтительный способ
public sealed class MySafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private MySafeHandle() : base(true) { }
protected override bool ReleaseHandle() => NativeMethods.CloseHandle(handle);
}
[DllImport("kernel32")]
public static extern MySafeHandle CreateFile(...);
→ Автоматически интегрируется с GC, thread-safe, предотвращает утечки при AppDomain unload.
3. Span<T>, ReadOnlySpan<T>, Memory<T>, ReadOnlyMemory<T>
Сравнительная таблица
| Тип | Где может храниться | Управляемый? | Може ли быть полем? | Може ли выходить из метода? | Использование |
|---|
Span<T> | стек, неуправляемая память, массив | нет — ref struct | ❌ | ❌ (кроме ref return) | Быстрые операции в пределах метода |
ReadOnlySpan<T> | то же | нет — ref struct | ❌ | ❌ | Только чтение |
Memory<T> | управляется GC | ✅ | ✅ | ✅ | Передача между асинхронными операциями |
ReadOnlyMemory<T> | то же | ✅ | ✅ | ✅ | Только чтение |
Создание
Код ITЗагрузка примера кода…
Ограничения Span<T>:
- Нельзя:
- быть полем класса/обычной структуры
- быть элементом массива
- использоваться в
async/yield методах
- передаваться в
lambda, local function, try/catch/finally (в некоторых случаях)
- использоваться как обобщённый аргумент (
List<Span<T>> — ошибка)
→ Решение: использовать Memory<T> для передачи, .Span — для локальной обработки.
Пример безопасного использования
public static bool TryParseHeader(ReadOnlySpan<byte> data, out int length)
{
if (data.Length < 4) { length = 0; return false; }
length = BinaryPrimitives.ReadInt32LittleEndian(data);
return length >= 0;
}
4. stackalloc, ArrayPool<T>, MemoryPool<T>
stackalloc
Span<byte> buffer = stackalloc byte[1024];
int n = Math.Min(count, 1024);
Span<byte> temp = stackalloc byte[n];
- Максимальный размер по умолчанию: ~1 МБ (ограничение Windows x64), но зависит от платформы.
- Не вызывает
GC, но переполнение стека ⇒ StackOverflowException.
- В безопасном коде доступен только для
Span<T> с T = byte, sbyte, char, ushort, short, uint, int, ulong, long, nint, nuint, float, double.
ArrayPool<T>.Shared:
var array = ArrayPool<byte>.Shared.Rent(2048);
try
{
}
finally
{
ArrayPool<byte>.Shared.Return(array, clearArray: false);
}
- Пулы: глобальный (
Shared) или пользовательский (ArrayPool<T>.Create(maxArrayLength, maxArraysPerBucket)).
clearArray: true — обнулить массив перед возвратом (защита от утечки данных).
- Размеры — округляются до ближайшего бакета (256, 512, 1024, …, 1 048 576).
MemoryPool<T> (для Memory<T>):
var owner = MemoryPool<byte>.Shared.Rent(1024);
try
{
Memory<byte> mem = owner.Memory;
}
finally
{
owner.Dispose();
}
- Особенно полезно для
IAsyncEnumerable<T>, PipeWriter, ArrayPool-бэкенд.
5. System.Runtime.CompilerServices.Unsafe
Пакет: System.Runtime.CompilerServices.Unsafe (входит в .NET Standard 2.1+)
Основные методы
| Метод | Подпись | Назначение |
|---|
As<TFrom, TTo> | TTo As<TFrom, TTo>(ref TFrom source) | Интерпретация битов одного типа как другого (без копирования) |
AsPointer | void* AsPointer<T>(ref T value) | Получение указателя на переменную (без unsafe) |
AsRef | ref T AsRef<T>(void* source) | Преобразование указателя в ref |
SizeOf<T> | int SizeOf<T>() | Размер типа в байтах (без unsafe, работает для всех типов) |
Add<T> / Subtract<T> | ref T Add<T>(ref T source, int offset) | Арифметика указателей через ref |
Read<T> / Write<T> | T Read<T>(void* source) | Чтение/запись по неуправляемому адресу |
CopyBlock / InitBlock | void CopyBlock(void* dest, void* src, uint size) | memcpy, memset на уровне IL |
Примеры
Код ITЗагрузка примера кода…
Важно: Unsafe не отключает проверки границ массивов. Для этого — Unsafe.Add(ref arr[0], index) вместо arr[index].
6. Подсказки сборщику мусора и JIT
Атрибуты
| Атрибут | Применение | Эффект |
|---|
[ThreadStatic] | static поле | Каждый поток — своя копия |
[ThreadLocal] | static поле (ThreadLocal<T>) | То же, но с отложенной инициализацией |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | Метод | Подсказка JIT встраивать метод (не гарантируется) |
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | Метод | JIT применяет агрессивные оптимизации (даже в debug) |
[MethodImpl(MethodImplOptions.NoInlining)] | Метод | Запрет встраивания (для профилирования, Security) |
[MethodImpl(MethodImplOptions.NoOptimization)] | Метод | Отключить оптимизации (только для отладки) |
Методы GC
| Метод | Применение |
|---|
GC.KeepAlive(object) | Продлить жизнь объекта до точки вызова (часто после P/Invoke) |
GC.SuppressFinalize(object) | Отменить финализацию (в Dispose()) |
GC.ReRegisterForFinalize(object) | Повторно поставить в очередь финализации |
GC.AllocateUninitializedArray<T>(int, bool) | Выделить массив без инициализации default(T) (для struct, где default = 0) |
GC.AllocateArray<T>(int, bool) | Выделить массив, избегая GC pressure (в .NET 6+) |
GC.GetGCMemoryInfo() | Статистика по поколениям, паузам, фрагментации |
JIT-подсказки через код
- Использовать
ReadOnlySpan<T> вместо string/array для hot-путей.
- Избегать виртуальных вызовов в циклах (JIT не может devirtualize без
sealed или final).
- Использовать
ValueTask вместо Task для синхронного случая.
- Размещать часто используемые поля вместе (cache-line locality).
7. ref struct, ref fields, scoped (C# 11–12)
ref struct — правила
- Может содержать только:
- другие
ref struct
ref T, ref readonly T
T* (в unsafe)
fixed-буферы
- Нельзя:
- реализовывать интерфейсы (даже
IDisposable — но можно ref-расширения Dispose)
- быть захваченным в замыкании
- использоваться в
async/yield
- быть обобщённым параметром
- быть возвращённым из метода (кроме
ref return)
ref fields (C# 11)
public ref struct RefWrapper
{
public ref int Value;
}
- Позволяет хранить ссылку на переменную внутри
ref struct.
- Требует осторожности: ссылка может стать "висячей".
scoped (C# 12)
Указывает, что ссылка не покидает текущую область:
public static scoped ref int Max(scoped ref int a, scoped ref int b)
=> ref (a > b ? ref a : ref b);
public static void Process(scoped Span<byte> data)
{
}
Поддерживаемые типы:
ref T, ref readonly T
Span<T>, ReadOnlySpan<T>
scoped ref struct (в параметрах)
field (C# 12) — доступ к неявному полю автосвойства
public string Name
{
get => field?.ToUpperInvariant() ?? "";
init => field = value?.Trim() ?? "";
}
→ Работает только в get/set/init, не в обычных свойствах с явным полем.
Асинхронность
1. Основные типы асинхронных операций
| Тип | Применение | Аллокации | Примечания |
|---|
Task | Асинхронная операция без результата | 1 (при первом await) | Кэшированная Task.CompletedTask — использовать вместо new Task() |
Task<T> | Асинхронная операция с результатом T | 1 (при первом await) | Если T — значимый тип, упаковка при await |
ValueTask | Оптимизация для синхронного случая | 0 (если синхронно завершено) | Обёртка: либо Task, либо ManualResetValueTaskSourceCore<T> |
ValueTask<T> | То же, с результатом | 0 (если синхронно) | Предпочтительно для hot-путей и интерфейсов (например, IAsyncEnumerable.MoveNextAsync()) |
IAsyncEnumerable<T> | Асинхронный поток значений | 1–N (в зависимости от реализации) | await foreach, yield return await, ConfigureAwait(false) в цикле |
ConfiguredCancelableAsyncEnumerable<T> | IAsyncEnumerable<T> с ConfigureAwait и CancellationToken | — | Через .ConfigureAwait(false).WithCancellation(ct) |
Когда использовать ValueTask вместо Task
- Операция часто завершается синхронно (например, кэш hit).
- Метод вызывается в hot-path (более 10⁶ вызовов/сек).
- Интерфейс допускает
ValueTask (например, IAsyncDisposable.DisposeAsync() возвращает ValueTask).
Ограничения ValueTask
- Нельзя
await дважды (кроме .AsTask()).
- Нельзя использовать
Task.Wait(), .Result, .GetAwaiter().GetResult() без проверки.
- Не поддерживает
Task.WhenAll, Task.WhenAny напрямую — конвертировать через .AsTask().
2. async/await — синтаксис и семантика
Синтаксис
public async Task<int> DownloadAndParseAsync(string url, CancellationToken ct = default)
{
using var client = new HttpClient();
string json = await client.GetStringAsync(url, ct).ConfigureAwait(false);
return JsonSerializer.Deserialize<int>(json);
}
Ключевые правила
| Правило | Примечание |
|---|
async метод должен содержать как минимум один await, или возвращать завершённую задачу напрямую | Иначе предупреждение IDE0078 (но компилируется) |
await распаковывает Task<T> → T, ValueTask<T> → T, Task/ValueTask → void | Исключение пробрасывается напрямую (не завёрнуто в AggregateException) |
await захватывает SynchronizationContext по умолчанию | В UI — возвращает в поток UI; в ASP.NET — в контекст запроса |
ConfigureAwait(false) — отменяет захват контекста | Рекомендуется в библиотеках, особенно в цепочках await |
async void — только для event handlers | В остальных случаях — async Task (иначе невозможно await, обработка исключений — через AppDomain.UnhandledException) |
State machine (IL)
- Компилятор генерирует
struct-машину состояний:
- Поля — параметры метода, локальные переменные,
awaiter, текущее состояние.
- Метод
MoveNext() — реализует логику после await.
- Размер state machine = сумма размеров захваченных переменных (оптимизация:
ref struct в локальных async функциях в C# 12+).
3. CancellationToken и отмена операций
Создание
var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct1, ct2);
Использование
public async Task<string> FetchAsync(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
using var client = new HttpClient();
return await client.GetStringAsync("https://...", ct).ConfigureAwait(false);
}
Ручная проверка
while (!ct.IsCancellationRequested)
{
await Task.Delay(100, ct).ConfigureAwait(false);
ct.ThrowIfCancellationRequested();
}
Обработка исключения
try
{
await operation(ct);
}
catch (OperationCanceledException ex) when (ex.CancellationToken == ct)
{
return default;
}
Важно
- Передавать
CancellationToken явно во все асинхронные вызовы.
- Не игнорировать
ct в циклах и долгих синхронных операциях.
- Использовать
CancellationToken.Register только если нет async-альтернативы (дорого: аллокация делегата).
4. IAsyncEnumerable<T> и асинхронные потоки
Создание
public async IAsyncEnumerable<int> GeneratePrimesAsync(
[EnumeratorCancellation] CancellationToken ct = default)
{
for (int i = 2; ; i++)
{
ct.ThrowIfCancellationRequested();
if (IsPrime(i))
yield return i;
await Task.Yield();
}
}
Использование
await foreach (int prime in GeneratePrimesAsync(ct)
.ConfigureAwait(false)
.WithCancellation(ct))
{
Console.WriteLine(prime);
if (prime > 1000) break;
}
Особенности
[EnumeratorCancellation] — внедряет CancellationToken в метод (если вызван через .WithCancellation(ct)).
ConfigureAwait(false) и WithCancellation(ct) — chainable.
yield return await — разрешено:
await foreach (var item in source)
yield return await TransformAsync(item, ct);
IAsyncEnumerator<T> реализует IAsyncDisposable → await using.
Производительность
Channel<T> предпочтительнее, если нужна очередь producer/consumer.
AsyncEnumerable — для ленивых, pull-based потоков.
5. Продвинутые механизмы — IValueTaskSource, ManualResetValueTaskSourceCore
Когда нужно
- Реализация
async-операций без аллокаций даже при асинхронном завершении.
- Высокопроизводительные библиотеки (сетевые протоколы, сериализаторы).
Пример
Код ITЗагрузка примера кода…
ManualResetValueTaskSourceCore<T>:
- Внутренний state machine, оптимизированный для повторного использования.
- Поддерживает
SetResult, SetException, Reset.
- Не потокобезопасен — требуется внешняя синхронизация.
6. Вспомогательные типы и методы
| Тип / Метод | Назначение |
|---|
Task.CompletedTask | Завершённая Task (кэширована) — использовать вместо Task.FromResult(0) |
Task.FromResult<T>(T) | Завершённая Task<T> |
Task.FromException<T>(Exception) | Завершённая с ошибкой |
Task.FromCanceled<T>(CancellationToken) | Отменённая задача |
Task.Yield() | Возвращает управление в синхронизатор (полезно в UI для прогресса) |
Task.Delay(TimeSpan, CancellationToken) | Асинхронная задержка с отменой |
Task.WhenAll(params Task[]) | Ожидание всех, возвращает Task/Task<T[]> |
Task.WhenAny(params Task[]) | Возвращает первую завершённую |
TaskCompletionSource<T> | Вручную управляемая задача (для оборачивания событий, callback-API) |
PeriodicTimer (.NET 6+) | await timer.WaitForNextTickAsync(ct) — замена Timer + TaskCompletionSource |
Task.WaitAsync(TimeSpan, CancellationToken) (.NET 6+) | Таймаут без CancellationTokenSource |
Пример TaskCompletionSource
var tcs = new TaskCompletionSource<bool>();
someEvent += (_, args) => tcs.SetResult(args.Success);
await tcs.Task;
7. Паттерны и анти-паттерны
✅ Рекомендованные
- Async all the way down — не смешивать
async и .Result/.Wait().
- ConfigureAwait(false) в библиотеках — избегать захвата контекста.
- Передача
CancellationToken явно — не использовать CancellationToken.None по умолчанию в публичных API.
- Использовать
ValueTask в интерфейсах, если реализация может быть синхронной.
IAsyncDisposable + await using — для асинхронного освобождения.
❌ Анти-паттерны
async void вне event handlers.
.Result или .Wait() в async-методах ⇒ deadlock (в presence of SynchronizationContext).
- Игнорирование
CancellationToken.
Task.Run в библиотеках без необходимости (нарушает планирование).
new Task(...).Start() — предпочтительно Task.Run или Task.Factory.StartNew с явным TaskScheduler.
Безопасное синхронное ожидание (только в крайних случаях)
var task = asyncMethod();
var result = Task.Run(() => task).GetAwaiter().GetResult();
var result = task.AsTask().GetAwaiter().GetResult();
→ Избегает deadlock, но всё равно блокирует поток.
8. Современные инструменты (.NET 6–8)
| Фича | Применение |
|---|
IProgress<T> + Progress<T> | Отчёт о прогрессе из async-методов (автоматически маршрутизирует в UI-поток) |
Channel<T> | Producer/consumer с поддержкой async (Channel.CreateBounded, CreateUnbounded) |
AsyncLock (из Nito.AsyncEx) | Асинхронная блокировка (вместо lock) |
SemaphoreSlim.WaitAsync | Асинхронное ограничение параллелизма |
Parallel.ForEachAsync (.NET 6) | Асинхронный параллелизм с контролем степени параллелизма |
Channel<T> пример:
var channel = Channel.CreateBounded<int>(10);
_ = Task.Run(async () =>
{
await foreach (var item in channel.Reader.ReadAllAsync(ct))
Process(item);
});
await channel.Writer.WriteAsync(42, ct);
Метапрограммирование
1. Атрибуты — синтаксис и применение
Базовый синтаксис
[<целевой_объект>:] <ИмяАтрибута>(<позиционные_параметры>, <именованные> = <значение>)
Целевые объекты (target)
| Цель | Применяется к |
|---|
assembly | using-директиве (в начале файла) — атрибут сборки |
module | тоже — атрибут модуля (редко) |
field | полю (например, в автосвойстве) |
event | событию |
method | методу, конструктору, оператору, финализатору |
param | параметру метода |
property | свойству |
return | возвращаемому значению |
typevar | параметру типа (where T : [NotNull] object) — в обобщениях |
Пример:
[assembly: AssemblyCopyright("© 2025")]
public string Name
{
[return: NotNull]
get => _name ??= "";
[param: AllowNull]
set => _name = value;
}
2. Встроенные атрибуты .NET и C#
2.1. Управление видимостью и устареванием
| Атрибут | Параметры | Эффект |
|---|
[Obsolete] | message: string, error: bool = false | Предупреждение/ошибка компиляции при использовании |
[EditorBrowsable(EditorBrowsableState.Never)] | — | Скрывает из IntelliSense (не влияет на компиляцию) |
[Browsable(false)] | — | Для дизайнеров (WinForms/WPF) |
2.2. Условная компиляция
| Атрибут | Параметры | Эффект |
|---|
[Conditional("DEBUG")] | conditionName: string | Метод и его вызовы удаляются, если символ не определён (#define DEBUG) |
[Conditional("TRACE")] | — | То же для System.Diagnostics.Trace |
Важно: метод должен возвращать void и не иметь out-параметров (но допускает ref/in).
2.3. P/Invoke и нативный код
| Атрибут | Параметры | Эффект |
|---|
[DllImport("kernel32")] | EntryPoint, CharSet, CallingConvention, SetLastError | Импорт нативной функции |
[MarshalAs(UnmanagedType.LPStr)] | — | Указывает маршаллинг для параметра/поля/возврата |
[StructLayout(LayoutKind.Sequential, Pack = 1)] | — | Контроль макета памяти структуры |
[SuppressGCTransition] (.NET 5+) | — | Отключает переход GC-модуля для static extern методов (повышает производительность) |
2.4. Оптимизации и JIT
| Атрибут | Параметры | Эффект |
|---|
[MethodImpl(MethodImplOptions.AggressiveInlining)] | — | Подсказка JIT встраивать метод |
[MethodImpl(MethodImplOptions.NoInlining)] | — | Запрет встраивания |
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | — | Применять агрессивные оптимизации даже в debug |
[ThreadStatic] | — | static поле — по одному экземпляру на поток |
[ContextStatic] | — | Одна копия на ExecutionContext (устарел, AsyncLocal<T> предпочтительнее) |
[AsyncMethodBuilder(typeof(MyBuilder))] | — | Указывает кастомный билдер для async-методов |
2.5. Nullable Reference Types (NRT) — аннотации
| Атрибут | Применяется к | Эффект |
|---|
[NotNull] | параметру, полю, свойству | После вызова значение точно не null |
[MaybeNull] | возвращаемому значению | Может вернуть null, даже если тип не помечен как T? |
[AllowNull] | параметру | Разрешает передавать null, даже если тип T (не T?) |
[DisallowNull] | параметру | Запрещает null, даже если тип T? |
[NotNullWhen(true)] | параметру out/ref | Если метод вернул true, то параметр не null |
[MaybeNullWhen(false)] | параметру out/ref | Если метод вернул false, параметр может быть null |
[DoesNotReturn] | методу | Метод никогда не возвращается (например, throw new ...) |
[DoesNotReturnIf(true)] | параметру bool | Если аргумент true, метод не возвращается |
[MemberNotNull("Field1", "Property2")] | методу | После вызова указанные члены точно не null |
[MemberNotNullWhen(true, "Field")] | методу | Если вернул true, член не null |
Пример:
[return: MaybeNull]
public T GetOrDefault<T>(string key) { ... }
public bool TryGetValue<T>(string key, [MaybeNullWhen(false)] out T value)
{
value = default;
return found;
}
2.6. Записи и обязательные члены (C# 11+)
| Атрибут | Эффект |
|---|
[SetsRequiredMembers] | Разрешает вызывать конструктор, не инициализируя required-члены — они будут установлены вызывающим кодом |
[RequiredMember] | Генерируется компилятором на типе с required-членами — не используется вручную |
Пример:
public record Person(string Name, int Age);
[SetsRequiredMembers]
public static Person CreateAdmin() => new() { Name = "Admin", Age = 99 };
2.7. Интерполированные строки (C# 10–12)
| Атрибут | Применение | Эффект |
|---|
[InterpolatedStringHandler] | ref struct | Позволяет реализовать кастомную обработку интерполированных строк без аллокаций |
[InterpolatedStringHandlerArgument("sb")] | Параметру обработчика | Передаёт дополнительные аргументы (например, StringBuilder) |
Пример:
[InterpolatedStringHandler]
public ref struct LogHandler
{
public LogHandler(int literalLength, int formattedCount, StringBuilder sb) { ... }
public void AppendLiteral(string s) => sb.Append(s);
public void AppendFormatted<T>(T value) => sb.Append(value);
}
public static void Log([InterpolatedStringHandlerArgument("")] ref LogHandler handler);
2.8. Инициализация и анализ
| Атрибут | Эффект |
|---|
[ModuleInitializer] | Статический метод без параметров — вызывается при загрузке модуля (до любого другого кода) |
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(T))] | Указывает linker'у, что тип T используется динамически — сохранить заданные члены |
[UnscopedRef] (C# 12) | Разрешает возврат ref из метода, помеченного scoped ref — отменяет ограничение |
2.9. Compile-time проверки (.NET 7–8)
| Атрибут | Применение | Эффект |
|---|
[StringSyntax(StringSyntaxAttribute.Uri)] | параметру string | Подсказывает анализаторам/IDE, что строка должна быть URI (IntelliSense, проверка в source generators) |
[StringSyntax(StringSyntaxAttribute.Json)] | — | Аналогично для JSON |
[StringSyntax(StringSyntaxAttribute.Regex)] | — | Проверка корректности регулярки на этапе компиляции |
[Experimental("MY001")] | типу/члену | Помечает как экспериментальный — требует #pragma warning disable EXXX для использования |
[ConstantExpected] (.NET 8) | параметру | Подсказка, что ожидается compile-time константа (анализатор может проверить) |
Доступные StringSyntax:
Uri, Regex, Json, DateTimeFormat, TimeFormat, NumericFormat, CompositeFormat, MailAddress
3. Пользовательские атрибуты
Создание
Код ITЗагрузка примера кода…
Правила
- Должен наследоваться от
System.Attribute.
- Имя заканчивается на
Attribute (но при применении суффикс можно опустить: [My]).
- Позиционные параметры — только через конструктор.
- Именованные — через публичные свойства/поля.
- Параметры конструктора и свойств должны быть compile-time константами:
bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort, enum, Type, object, массивы этих типов.
Чтение в runtime (рефлексия)
var attr = typeof(MyClass).GetCustomAttribute<MyAttribute>();
if (attr?.Name == "Test") { ... }
Чтение в source generator (compile-time)
var attr = context.SemanticModel.GetDeclaredSymbol(typeSyntax)!.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "MyAttribute");
var name = attr?.ConstructorArguments[0].Value as string;
4. Рефлексия — основные API и производительность
Получение типов
| Метод | Примечание |
|---|
typeof(T) | Compile-time, без аллокаций |
obj.GetType() | Runtime, возвращает RuntimeType |
Type.GetType("Namespace.Type, Assembly") | По строке (медленно, избегать в hot-path) |
Создание экземпляров
| Метод | Производительность | Примечание |
|---|
new T() | наилучшая | Только для where T : new() |
Activator.CreateInstance<T>() | ~10× медленнее new | Использует кэшированный Func<T> в .NET Core+ |
Activator.CreateInstance(type) | ~100× медленнее | Возвращает object, упаковка для value types |
System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(type) | Очень быстро | Не вызывает конструктор — только для сериализаторов |
Вызов методов
| Метод | Производительность | Примечание |
|---|
| Прямой вызов | 1× | — |
MethodInfo.Invoke | ~1000× медленнее | Аллокации, проверки безопасности |
Delegate.CreateDelegate + вызов | ~10× медленнее | Кэширование делегата критично |
Expression.Lambda(...).Compile() | ~5× медленнее | Создаёт IL-код; CompileToMethod — для статики |
Кэширование делегатов
private static readonly Func<MyClass, int> _getter =
(Func<MyClass, int>)Delegate.CreateDelegate(
typeof(Func<MyClass, int>),
typeof(MyClass).GetMethod("GetValue")!);
Альтернативы (без рефлексии)
interface + generic-ограничения.
source generators — генерация partial методов.
System.Text.Json.Serialization.Metadata — метаданные для сериализации без рефлексии.
5. Source Generators (C# 9+)
Архитектура
Код ITЗагрузка примера кода…
Incremental mode (предпочтительно)
SyntaxValueProvider.CreateSyntaxProvider — фильтрация и преобразование AST.
CompilationProvider — доступ к символам (SemanticModel).
Combine, Select, Collect, Where — композиция pipeline.
- Автоматический кэш промежуточных результатов.
Триггеры генерации
- Изменение синтаксического дерева.
- Изменение семантики (референсы, using'и).
- Изменение дополнительных файлов (
AdditionalText).
Генерация
- Только
partial типы и методы.
- Имена файлов:
<имя>.<хэш>.g.cs — не должны конфликтовать.
- Диагностика:
context.ReportDiagnostic(Diagnostic.Create(...)).
Пример — генерация Deconstruct для класса с [GenerateDeconstruct]:
[ModuleInitializer]
internal static void Init()
{
}
6. Директивы препроцессора
| Директива | Применение |
|---|
#define SYMBOL | Определить символ (только в начале файла) |
#undef SYMBOL | Отменить символ |
#if SYMBOL / #elif / #else / #endif | Условная компиляция |
#warning "text" | Предупреждение компиляции |
#error "text" | Ошибка компиляции |
#line 200 / #line default / #line hidden | Управление отладочной информацией (для генераторов) |
#nullable enable / disable / restore | Управление NRT на уровне файла/блока |
Стандартные символы
DEBUG, TRACE — определяются в проекте.
NET6_0, NET7_0, NET8_0 — целевая платформа (автоматически).
WINDOWS, LINUX, BROWSER — ОС/среда (.NET 5+).
7. Caller-атрибуты (compile-time подстановка)
| Атрибут | Подставляет |
|---|
[CallerFilePath] | Путь к файлу вызова (string) |
[CallerLineNumber] | Номер строки (int) |
[CallerMemberName] | Имя метода/свойства (string) |
Пример:
public void Log(string message,
[CallerMemberName] string member = "",
[CallerFilePath] string file = "",
[CallerLineNumber] int line = 0)
{
Console.WriteLine($"{file}({line}): {member} → {message}");
}
→ Вызов Log("test") → "Program.cs(42): Main → test".
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Справочники — Справочник по ASP.NET, Справочник по C++, Справочник по LINQ, Справочник по PHP, Справочник по конфигурациям в C#, Справочник по Smalltalk.