Руководство и документация по языку программирования C#
Фундаментальный разбор языка C# и платформы .NET: тип данных, объявление типов, современный синтаксис, управление памятью, асинхронность и функциональные возможности LINQ.
- 1. Экосистема C# и виртуальная машина CLR
- 2. Система типов: Значимые и ссылочные типы
- 3. Объектно-ориентированное программирование и интерфейсы
- 4. Асинхронное программирование: async, await и Task
- 5. LINQ (Language Integrated Query) и декларативная обработка
- 6. Управление памятью: Garbage Collector, Span и Memory
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. Однако на рабочем уровне вся система разделена на две фундаментальные категории, определяющие место хранения данных в оперативной памяти:
- Значимые типы (Value Types): Включают числовые типы (
int,float,decimal), структуры (struct), логический тип (bool) и перечисления (enum). Переменные этого типа хранят непосредственное значение. Как правило, данные значимых типов размещаются в стеке (Stack) — высокоскоростной области памяти с фиксированным размером. При передаче значимого типа в метод происходит полное копирование его содержимого. - Ссылочные типы (Reference Types): Включают классы (
class), интерфейсы (interface), строки (string), массивы и делегаты. Переменная ссылочного типа хранит лишь адрес (указатель), ссылающийся на область памяти в куче (Heap), где находятся реальные данные объекта. При передаче ссылочного типа передается лишь копия адреса, поэтому изменения объекта внутри метода отражаются на исходном объекте.
Ниже представлен пример объявления классов и структур с выделением ключевых токенов синтаксиса:
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());