Перейти к основному содержимому

Справочник по C#


Назначение

Справочник-шпаргалка по C# — типы, синтаксис, стандартная библиотека, типовые паттерны. Не заменяет пошаговое обучение. Учебный курс: раздел.

Полный официальный каталог: справочник языка на Learn (869 разделов) · справочник .NET API (BCL).


Краткое пояснение

Компоненты и ключевые особенности языка.


Полная карта C# Language Reference (Microsoft Learn)

Ниже — маршрут по официальному справочнику языка C# "по максимуму" — ключевые слова, операторы/выражения, инструкции языка, параметры компилятора, сообщения компилятора и спецификация.

Ядро справочника

Ключевые слова, операторы и инструкции

Компилятор, документация, спецификация

Полный API .NET


Справочные таблицы

Лексика, типы, операторы, ключевые слова

1. Типы данных

Встроенные (примитивные) типы и их псевдонимы
C# псевдонимСистемный типРазмер (бит)Диапазон / Примечание
boolSystem.Boolean8true / false
sbyteSystem.SByte8−128 … 127
byteSystem.Byte80 … 255
shortSystem.Int1616−32 768 … 32 767
ushortSystem.UInt16160 … 65 535
intSystem.Int3232−2 147 483 648 … 2 147 483 647
uintSystem.UInt32320 … 4 294 967 295
longSystem.Int6464−2⁶³ … 2⁶³−1
ulongSystem.UInt64640 … 2⁶⁴−1
nintSystem.IntPtr32/64зависит от архитектуры (указательный размер)
nuintSystem.UIntPtr32/64зависит от архитектуры
charSystem.Char16UTF-16 символ (один элемент кодировки)
floatSystem.Single32~±1.5 × 10⁻⁴⁵ … ±3.4 × 10³⁸, 7 цифр точности
doubleSystem.Double64~±5.0 × 10⁻³²⁴ … ±1.7 × 10³⁰⁸, 15–16 цифр
decimalSystem.Decimal128±1.0 × 10⁻²⁸ … ±7.9 × 10²⁸, 28–29 цифр, для финансовых вычислений
stringSystem.Stringrefссылка на неизменяемую UTF-16 строку
objectSystem.Objectrefкорневой тип всех ссылочных и упакованных значений

Категории типов
  • Значимые (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Класс — запрет на наследование; метод — запрет дальнейшего переопределения
partialclass, 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. Операторы

Приоритет (уменьшается сверху вниз)
ПриоритетОператорыПримечания
20x.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Унарные, приведения, указательные операции
18x * y, x / y, x % yМультипликативные
17x + y, x - yАддитивные
16x << y, x >> yПобитовые сдвиги
15x < y, x <= y, x > y, x >= y, is, asСравнение, проверка типа
14x == y, x != yРавенство
13x & yПобитовое И (для bool — несокращённое логическое И)
12x ^ yПобитовое исключающее ИЛИ (XOR)
11x | yПобитовое ИЛИ (для bool — несокращённое логическое ИЛИ)
10x && yСокращённое логическое И
9x || yСокращённое логическое ИЛИ
8x ?? yNull-coalescing
7c ? a : bТернарный условный
6x = y, x op= y, x ??= y, x ?= y (нет), => — не операторПрисваивание (все возвращают присвоенное значение)
5yield 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 ??= ba = 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);Принадлежит типу
readonlypublic readonly Guid Id = Guid.NewGuid();Инициализируется только в объявлении или конструкторе
constpublic const double Pi = 3.141592653589793;Компилируемая константа (только для примитивов и string)
volatileprivate 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, выделяется инлайн

Инициализация
  • Порядок инициализации:
    1. Статические поля → статический конструктор
    2. Поля экземпляра → конструктор базового класса (base(...)) → конструктор текущего класса
  • required поля не инициализируются автоматически — контроль на этапе компиляции (если не указано при создании, ошибка CS9035).

2. Свойства (Properties)

Синтаксис
[<атрибуты>] [<модификаторы>] <тип> <Имя>
{
[<модификаторы>] get { <тело> }
[<модификаторы>] set { <тело> }
[<модификаторы>] init { <тело> }
}
// Или expression-bodied:
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/overridepublic 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;
}

Стандартные делегаты
ДелегатСигнатураПрименение
EventHandlervoid 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)

Синтаксис
[<атрибуты>] [<модификаторы>] <возвращаемый тип> <Имя>([<параметры>])
{
<тело>
}
// Или expression-bodied:
public int Square(int x) => x * x;

Параметры
Способ передачиСинтаксисПоведение
По значению (по умолчанию)void M(int x)Копия значения (для ссылочных — копия ссылки)
refvoid M(ref int x)Передача по ссылке — изменения влияют на аргумент
outbool TryParse(string s, out int result)Выходной параметр — должен быть проинициализирован в методе
invoid M(in ReadOnlySpan<byte> data)ref readonly — нельзя модифицировать, но избегается копирование
paramsvoid 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 : classclass Box<T> where T : classСсылочный тип (включая string, делегаты, массивы)
where T : class?(C# 9+, NRT)Ссылочный тип, допускает null
where T : structSpan<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 : IDisposablevoid Use<T>(T obj) where T : IDisposableДолжен реализовывать интерфейс
where T : notnull(C# 8+)Запрещает null даже в nullable-контексте
where T : Uvoid Copy<T, U>(T src, U dst) where T : UT должен быть 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)

~ClassName()
{
// Освобождение неуправляемых ресурсов
}
  • Компилируется в 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 && ba ? b : false при наличии true/false).
  • implicit приведение должно быть безопасным (без потерь, исключений).
  • Нельзя перегрузить — =, &&, \|\|, ??, ?., ->, ?:, new, typeof, sizeof, is, as, checked, unchecked.

8. Вложенные типы

public class Outer
{
private int _x;
public class Nested { /* имеет доступ к private Outer._x */ }
public struct NestedStruct { }
public interface INested { }
public delegate void NestedDelegate();
public enum NestedEnum { A, B }
}
// Использование: Outer.Nested, Outer.NestedStruct и т.д.
  • Доступность по умолчанию: private.
  • Вложенные типы наследуют generic-параметры внешнего типа:
class Outer<T>
{
class Inner : IEnumerable<T> { ... } // 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 classrecord 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)Нет (структуры не наследуются)
PrintMembersprotected virtual bool PrintMembers(StringBuilder sb)Нет (но можно определить)

Управление памятью и производительность

1. Упаковка (boxing) и распаковка (unboxing)

Что происходит
  • Boxing — преобразование значимого типа в object или интерфейс → выделение памяти в куче, копирование значения, возврат ссылки.
int x = 42;
object boxed = x; // boxing: new object { int m_value = 42 }
  • Unboxing: извлечение значения из упакованного объекта → проверка типа (isinst), копирование значения.
int y = (int)boxed; // unboxing: проверка, что boxed — boxed int, затем копирование

Стоимость
ОперацияСтоимостьПримечание
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+0000U+007F (0–127)
  • Целые (sbyte, byte, short, ushort, int): значения от –128 до 127
object a = 100, b = 100;
Console.WriteLine(ReferenceEquals(a, b)); // true
object c = 300, d = 300;
Console.WriteLine(ReferenceEquals(c, d)); // false

Как избежать
  • Использовать обобщённые коллекции (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]; // 1 KB на стеке
// или с размером во время выполнения:
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
{
// использовать array
}
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)Интерпретация битов одного типа как другого (без копирования)
AsPointervoid* AsPointer<T>(ref T value)Получение указателя на переменную (без unsafe)
AsRefref 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 / InitBlockvoid 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; // разрешено с C# 11
}
  • Позволяет хранить ссылку на переменную внутри 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)
{
// Нельзя: Func<byte> f = () => data[0];
}

Поддерживаемые типы:

  • 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>Асинхронная операция с результатом T1 (при первом 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/ValueTaskvoidИсключение пробрасывается напрямую (не завёрнуто в 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);
// большинство .NET API поддерживают ct
}

Ручная проверка
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> реализует IAsyncDisposableawait 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();
// или (в .NET 5+):
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)
ЦельПрименяется к
assemblyusing-директиве (в начале файла) — атрибут сборки
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
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);
// Использование: Log($"Value: {x}");

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)Очень быстроНе вызывает конструктор — только для сериализаторов

Вызов методов
МетодПроизводительностьПримечание
Прямой вызов
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.