🟪 C#
Полный справочник: типы и nullable, строки, управляющие конструкции, ООП, record, коллекции и LINQ, async/await, исключения, файлы, DI, тесты и экосистема .NET.
Шпаргалки · Программирование · #c# #csharp #dotnet #backend
Запуск и окружение
dotnet new console -n App dotnet run dotnet build -c Release
dotnet add package Serilog dotnet test dotnet publish -c Release -r linux-x64 --self-contained
dotnet new list dotnet watch run
IDE: Visual Studio, Rider, VS Code (C# Dev Kit). Версии: .NET 8 LTS, .NET 9, .NET 10 LTS.
Минимальная программа (top-level statements)
Console.WriteLine("Привет");
Классический вид: namespace App; class Program { static void Main(string[] args) { } }. Файловые namespace X; и неявные using включены в современных шаблонах.
Типы данных
| Группа | Типы |
|---|---|
| Целые | sbyte, byte, short, int, uint, long, ulong, nint |
| Дробные | float, double, decimal (деньги, 28–29 знаков) |
| Прочие значения | bool, char, DateTime, DateOnly, TimeOnly, Guid, enum, struct |
| Ссылочные | string, object, массивы, классы, record, делегаты |
int n = 5; long big = 10_000_000_000; double d = 1.5; decimal price = 19.99m; bool ok = true; char c = 'a';
var list = new List<int>(); // тип выводится
int? maybe = null; // nullable value type
string? name = null; // nullable reference (включён <Nullable>enable</Nullable>)
int[] arr = { 1, 2, 3 }; int[,] grid = new int[3, 4];
(int Id, string Name) tuple = (1, "a"); var (id, nm) = tuple;
const int Max = 10; static readonly DateTime Start = DateTime.UtcNow;
int.Parse("42"); int.TryParse(text, out var value); Convert.ToInt32("42"); (double)5 / 2;
Типы-значения (struct) копируются, ссылочные (class) передаются по ссылке.
Строки
string s = "Привет";
$"{name} ({age:D3}) {price:C} {d:F2} {when:yyyy-MM-dd}" // интерполяция
s.Length; s[0]; s.Substring(1, 3); s.Contains("р"); s.StartsWith("П"); s.IndexOf('и'); s.ToUpper();
s.Trim(); s.Replace("a", "b"); s.Split(','); string.Join(", ", list); string.IsNullOrWhiteSpace(s);
string.Equals(a, b, StringComparison.OrdinalIgnoreCase);
var raw = """
Сырая строка с "кавычками"
"""; // 11+
var sb = new StringBuilder(); sb.Append("a").AppendLine("b"); sb.ToString();
Строки неизменяемы. Сравнение указывайте с StringComparison.
Операторы и управление
// ?? (по умолчанию для null), ??= , ?. (безопасный доступ), ! (подавить предупреждение), is, as, =>, .., ^
var city = user?.Address?.City ?? "не указан";
list ??= new();
if (x is > 0 and < 10) { } else if (obj is string str && str.Length > 0) { }
var label = x switch { < 0 => "минус", 0 => "ноль", _ => "плюс" }; // switch-выражение
string kind = shape switch { Circle c => $"r={c.R}", Rect { W: > 0 } r => "rect", _ => "?" };
for (int i = 0; i < 3; i++) { }
foreach (var item in list) { }
while (cond) { } do { } while (cond);
break; continue; goto (избегайте); lock (obj) { } using var f = File.OpenRead(path);
var slice = arr[1..3]; var last = arr[^1]; // диапазоны и индекс с конца
Классы, record, интерфейсы
public class User
{
public int Id { get; init; } // init: задаётся один раз
public string Name { get; set; } = "";
public string Label => $"@{Name}"; // вычисляемое
private readonly List<string> _tags = new();
public User(int id, string name) { Id = id; Name = name; }
public virtual void Print() => Console.WriteLine(Name);
}
public class Admin : User { public Admin(int id) : base(id, "admin") { } public override void Print() { base.Print(); } }
public record Point(int X, int Y); // значения по структуре, with-копирование
var p2 = p1 with { X = 5 };
public readonly struct Money(decimal amount) { public decimal Amount { get; } = amount; }
public interface IRepo<T> { Task<T?> FindAsync(int id); }
public abstract class Shape { public abstract double Area(); }
public enum Role { User = 1, Admin = 2 }
public class Svc(IRepo<User> repo) { } // первичные конструкторы (12+)
Модификаторы: public, internal, protected, private, sealed, static, abstract, virtual, override, readonly, partial, required.
Обобщения и делегаты
T Max<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) > 0 ? a : b;
Func<int, int> sq = x => x * x; Action<string> log = Console.WriteLine; Predicate<int> even = x => x % 2 == 0;
event EventHandler<EventArgs>? Changed; Changed?.Invoke(this, EventArgs.Empty);
Коллекции и LINQ
var list = new List<int> { 3, 1, 2 }; list.Add(4); list.Remove(1); list.Contains(2); list.Sort();
var dict = new Dictionary<string, int> { ["a"] = 1 }; dict.TryGetValue("a", out var v); dict.GetValueOrDefault("z");
var set = new HashSet<int>(); var q = new Queue<int>(); var st = new Stack<int>();
IReadOnlyList<int> ro = list;
var result = users
.Where(u => u.Age >= 18)
.OrderBy(u => u.Name).ThenByDescending(u => u.Age)
.Select(u => new { u.Id, u.Name })
.ToList();
users.Any(u => u.IsAdmin); users.All(u => u.Active); users.FirstOrDefault(u => u.Id == 1); users.Count(u => u.Age > 30);
users.GroupBy(u => u.Role).Select(g => new { Role = g.Key, N = g.Count() });
users.Sum(u => u.Salary); users.Max(u => u.Age); users.Distinct(); users.Take(10).Skip(20); users.Chunk(100);
orders.Join(users, o => o.UserId, u => u.Id, (o, u) => new { u.Name, o.Sum });
LINQ откладывает выполнение: запрос выполняется при ToList, foreach, Count.
Асинхронность
static async Task<string> LoadAsync(HttpClient http, CancellationToken ct = default)
{
var res = await http.GetAsync("https://example.com", ct);
res.EnsureSuccessStatusCode();
return await res.Content.ReadAsStringAsync(ct);
}
var results = await Task.WhenAll(urls.Select(u => LoadAsync(http))); // параллельно
await Task.Delay(1000); Task.Run(() => HeavyWork()); // CPU в пул потоков
await foreach (var x in StreamAsync()) { } // IAsyncEnumerable
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // отмена
Правила: async до конца (не блокируйте .Result, .Wait()), ConfigureAwait(false) в библиотеках, возвращайте Task, а не async void (кроме обработчиков событий). Параллельные циклы: Parallel.ForEachAsync.
Исключения
try { var n = int.Parse(text); }
catch (FormatException ex) when (text.Length > 0) { logger.LogWarning(ex, "..."); }
catch (Exception ex) { throw; } // throw; сохраняет стек (не throw ex)
finally { }
throw new InvalidOperationException("состояние неверно");
ArgumentNullException.ThrowIfNull(x);
public class AppException(string msg, Exception? inner = null) : Exception(msg, inner);
using (var conn = new SqlConnection(cs)) { } // Dispose гарантирован
Файлы, JSON, HTTP
File.ReadAllText(path); File.WriteAllText(path, text); File.Exists(path); Directory.CreateDirectory(dir);
await File.ReadAllLinesAsync(path); Path.Combine("a", "b");
var json = JsonSerializer.Serialize(obj, new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var obj = JsonSerializer.Deserialize<User>(json);
await http.GetFromJsonAsync<User>("/api/users/1"); await http.PostAsJsonAsync("/api/users", user);
HttpClient создавайте через IHttpClientFactory, не по экземпляру на запрос.
ASP.NET Core (минимальный API)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IRepo<User>, Repo>();
var app = builder.Build();
app.MapGet("/users/{id:int}", async (int id, IRepo<User> r) => await r.FindAsync(id) is { } u ? Results.Ok(u) : Results.NotFound());
app.MapPost("/users", (User u) => Results.Created($"/users/{u.Id}", u));
app.Run();
Время жизни DI: Singleton, Scoped (на запрос), Transient.
Тесты
[Fact] public void Adds() => Assert.Equal(3, Calc.Add(1, 2));
[Theory][InlineData(1, 2, 3)][InlineData(0, 0, 0)] public void Adds2(int a, int b, int e) => Assert.Equal(e, Calc.Add(a, b));
var repo = new Mock<IRepo<User>>(); repo.Setup(r => r.FindAsync(1)).ReturnsAsync(user);
xUnit, NUnit, MSTest, Moq, NSubstitute, FluentAssertions, Testcontainers, WebApplicationFactory.
Подводные камни
decimalдля денег,doubleприблизителен.- Сравнение строк без указания культуры.
- Захват переменных в замыканиях и отложенный LINQ.
- Блокировки
asyncкода (.Result): взаимные блокировки. - Забыли
Dispose(using). - Коллекция изменена во время
foreach.
Экосистема по задачам
| Задача | Что взять |
|---|---|
| Веб и API | ASP.NET Core, Minimal API, gRPC, SignalR |
| БД | Entity Framework Core, Dapper, Npgsql |
| UI | Blazor, MAUI, WPF, WinForms, Avalonia |
| Логи | Serilog, NLog, ILogger |
| Сообщения | MassTransit, RabbitMQ.Client, Confluent.Kafka |
| Паттерны | MediatR, FluentValidation, AutoMapper (Mapperly) |
| Задачи | Hangfire, Quartz.NET |
| Кэш | IMemoryCache, StackExchange.Redis |
| Тесты | xUnit, Moq, Testcontainers, Bogus |