Руководство и документация по языку программирования C#

Фундаментальный разбор языка C# и платформы .NET: тип данных, объявление типов, современный синтаксис, управление памятью, асинхронность и функциональные возможности LINQ.

Важное примечание по актуальности версий: Язык C# и платформа .NET обновляются каждый год осенью (выходят новые мажорные релизы C# и .NET). В новые версии регулярно добавляются синтаксические сахара, новые ключевые слова, оптимизации виртуальной машины CLR и паттерны. Рекомендуется сверять специфичные конструкции и новые фичи с официальной справочной документацией Microsoft Learn.

1. Экосистема C# и виртуальная машина CLR

Язык программирования C# — это строкотипизированный объектно-ориентированный язык с богатой историей и высокой производительностью. В отличие от языков, компилируемых напрямую в машинные инструкции процессора (как C или C++), код C# сперва компилируется в промежуточный байт-код, называемый CIL (Common Intermediate Language). Этот промежуточный код упаковывается в скомпилированные сборки — файлы с расширениями .dll или .exe.

За непосредственное выполнение этого промежуточного кода отвечает среда исполнения CLR (Common Language Runtime). Во время запуска программы специальный компилятор внутри CLR — JIT-компилятор (Just-In-Time) — переводит байт-код CIL в оптимизированные команды целевого процессора (x86, x64 или ARM) прямо в оперативной памяти устройства. Это позволяет коду запускаться с производительностью, максимально близкой к нативному коду, обеспечивая при этом полную кроссплатформенность под управлением современных версий .NET.

2. Система типов: Значимые и ссылочные типы

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

Ниже представлен пример объявления классов и структур с выделением ключевых токенов синтаксиса:

public class UserProfile
{
    public int Id { get; set; }
    public string Username { get; set; } = String.Empty;

    public void DisplayInfo()
    {
        Console.WriteLine($"Пользователь: {Username}, ID: {Id}");
    }
}
Разбор синтаксиса: ключевое слово public определяет уровень доступа. Определение свойства { get; set; } создает автосвойство с неявным скрытым полем. Ключевое слово class указывает на создание ссылочного типа данных в куче.

3. Объектно-ориентированное программирование и интерфейсы

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

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

public interface IRepository
{
    Task<UserProfile> GetByIdAsync(int id);
    void Save(UserProfile user);
}

public class UserRepository : IRepository
{
    public async Task<UserProfile> GetByIdAsync(int id)
    {
        await Task.Delay(100);
        return new UserProfile { Id = id, Username = "Admin" };
    }

    public void Save(UserProfile user)
    {
        Console.WriteLine("Сохранение в базу данных");
    }
}

4. Асинхронное программирование: async, await и Task

Асинхронность в C# построена на модели TAP (Task-based Asynchronous Pattern) с использованием ключевых слов async и await. Асинхронность позволяет выполнять длительные операции ввода-вывода (I/O), такие как обращения к базам данных, запросы по сети или чтение файлов с диска, без блокирования основного потока выполнения.

Когда поток встречает оператор await, выполнение метода приостанавливается, а сам поток освобождается и возвращается в пулл потоков (ThreadPool) для выполнения других полезных задач. После завершения операции I/O свободный поток из пулла подхватывает продолжение выполнения метода.

public async Task<string> FetchDataFromApiAsync(string url)
{
    using var client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(url);
    
    response.EnsureSuccessStatusCode();
    string content = await response.Content.ReadAsStringAsync();
    
    return content;
}

5. LINQ (Language Integrated Query) и декларативная обработка

LINQ — это встроенный в язык механизм декларативных запросов к коллекциям данных. LINQ позволяет писать выразительный код для фильтрации, сортировки, группировки и трансформации данных, независимо от того, где эти данные находятся: в массиве в памяти, файле XML или в реляционной базе данных SQL.

List<UserProfile> users = new()
{
    new() { Id = 1, Username = "Alex" },
    new() { Id = 2, Username = "Madalbekov" },
    new() { Id = 3, Username = "Muras" }
};

IEnumerable<string> filteredUsernames = users
    .Where(u => u.Id > 1)
    .OrderBy(u => u.Username)
    .Select(u => u.Username);

6. Управление памятью: Garbage Collector, Span и Memory

Управление памятью в C# автоматизировано с помощью сборщика мусора (Garbage Collector, GC). GC периодически сканирует кучу, находит объекты, на которые больше нет живых ссылок в коде, и освобождает занимаемую ими память.

Поколение GC Тип объектов Назначение и поведение
Generation 0 Короткоживущие объекты Временные переменные, локальные объекты внутри методов. Проверяется сборщиком наиболее часто.
Generation 1 Промежуточные объекты Объекты, выжившие после сборки в Gen 0. Служит буфером между короткоживущими и долгоживущими данными.
Generation 2 Долгоживущие объекты Статические переменные, синглтоны, кэши. Сборка мусора здесь происходит реже всего и требует больше ресурсов.

Для высокопроизводительных сценариев, где требуется избегать лишних аллокаций памяти в куче при работе со строками или массивами, в C# используются специальные конструкции ReadOnlySpan<T> и Memory<T>:

string sourceText = "FeXcharged:C#Docs";
ReadOnlySpan<char> slice = sourceText.AsSpan(0, 10);
Console.WriteLine(slice.ToString());