LEARN X · ЗА 17 МИН

C#

Весь C# на одной странице: типы, строки, LINQ, ООП, async/await и дженерики — экспресс-тур в закомментированном коде.

C# ("си шарп") — статически типизированный язык от Microsoft для платформы .NET. Здесь весь язык на одной странице: учим прямо по комментариям в коде.

Структура программы

Точка входа и вывод в консоль.

using System;                 // подключаем пространство имён (типы вроде Console)
using System.Collections.Generic; // ещё одно: List<T>, Dictionary<K,V>

namespace MyApp                // пространство имён группирует код
{
    class Program             // в C# весь код живёт внутри классов
    {
        // Main — точка входа; args — аргументы командной строки
        static void Main(string[] args)
        {
            Console.WriteLine("Привет, C#!"); // печать + перевод строки
            Console.Write("без перевода");      // печать без \n
        }
    }
}

// В C# 10+ можно top-level statements — без класса и Main:
// Console.WriteLine("Привет!"); // файл сам становится телом Main

Переменные и типы

Значимые (value) копируются, ссылочные (reference) делят объект.

int i = 42;            // целое 32 бита
long big = 9000000000; // целое 64 бита
double d = 3.14;       // число с плавающей точкой
decimal money = 9.99m; // точная десятичная (деньги), суффикс m
bool flag = true;      // логический: true / false
char c = 'A';          // один символ в одинарных кавычках
string s = "текст";    // строка (ссылочный тип)

var x = 10;            // var — тип выводится компилятором (тут int)
const double PI = 3.14159; // константа, менять нельзя

int? maybe = null;     // nullable value-тип: int или null
int val = maybe ?? 0;  // ?? — значение по умолчанию, если null  // val == 0

// Value-тип: копируется значение
int a = 5; int b = a; b = 99; // a по-прежнему 5

// Reference-тип: копируется ссылка на тот же объект
int[] arr1 = { 1, 2, 3 };
int[] arr2 = arr1; arr2[0] = 100; // arr1[0] тоже стало 100!

Строки

Интерполяция, методы и эффективная склейка через StringBuilder.

string name = "Аня";
int age = 25;

string greet = $"Привет, {name}, тебе {age}"; // интерполяция $""
string sum = $"2 + 2 = {2 + 2}";              // "2 + 2 = 4"
string path = @"C:\temp\file";  // @ — verbatim: без экранирования \

// Полезные методы:
"Hello".Length;            // 5
"Hello".ToUpper();         // "HELLO"
"Hello".ToLower();         // "hello"
"  hi  ".Trim();           // "hi"
"a,b,c".Split(',');        // ["a", "b", "c"]
string.Join("-", 1, 2, 3); // "1-2-3"
"Hello".Contains("ell");   // true
"Hello".Replace("l", "L"); // "HeLLo"
"Hello".Substring(1, 3);   // "ell"
"Hello"[0];                // 'H' — индексатор

// StringBuilder — для многих склеек (строки неизменяемы!)
using System.Text;
var sb = new StringBuilder();
sb.Append("раз");
sb.Append("-два");
sb.AppendLine("-три");
string result = sb.ToString(); // "раз-два-три\n"

Операторы и условия

int n = 7;

// if / else if / else
if (n > 10)
    Console.WriteLine("много");
else if (n > 5)
    Console.WriteLine("средне"); // сюда
else
    Console.WriteLine("мало");

// Тернарный оператор: условие ? если-да : если-нет
string parity = n % 2 == 0 ? "чёт" : "нечёт"; // "нечёт"

// Логические: && (и), || (или), ! (не)
bool ok = n > 0 && n < 100;

// Классический switch
switch (n)
{
    case 1:
        Console.WriteLine("один");
        break;             // break обязателен
    case 7:
    case 8:
        Console.WriteLine("7 или 8"); // сюда
        break;
    default:
        Console.WriteLine("другое");
        break;
}

// switch expression (C# 8+) — компактно, возвращает значение
string word = n switch
{
    1 => "один",
    7 => "семь",         // сюда  -> word == "семь"
    _ => "другое"        // _ — всё остальное (default)
};

Циклы

// for — счётчик
for (int i = 0; i < 3; i++)
    Console.WriteLine(i);   // 0, 1, 2

// foreach — перебор коллекции
foreach (var item in new[] { "a", "b", "c" })
    Console.WriteLine(item); // a, b, c

// while — пока условие истинно
int k = 0;
while (k < 3)
{
    Console.WriteLine(k);
    k++;                     // 0, 1, 2
}

// do-while — тело хотя бы раз, проверка в конце
int m = 0;
do
{
    Console.WriteLine(m);
    m++;
} while (m < 3);             // 0, 1, 2

// break — выйти из цикла; continue — к следующей итерации
for (int i = 0; i < 10; i++)
{
    if (i == 5) break;       // остановиться на 5
    if (i % 2 == 0) continue; // пропустить чётные
    Console.WriteLine(i);    // 1, 3
}

Массивы и коллекции

// Массив — фиксированный размер
int[] nums = new int[3];      // [0, 0, 0]
int[] primes = { 2, 3, 5, 7 };
primes[0];                    // 2
primes.Length;                // 4

// List<T> — динамический список
var list = new List<string> { "a", "b" };
list.Add("c");                // ["a", "b", "c"]
list.Remove("a");             // ["b", "c"]
list.Count;                   // 2
list.Contains("b");           // true

// Dictionary<K, V> — ключ -> значение
var ages = new Dictionary<string, int>
{
    ["Аня"] = 25,
    ["Боб"] = 30
};
ages["Аня"];                  // 25
ages.ContainsKey("Боб");      // true
ages["Вика"] = 22;            // добавить
if (ages.TryGetValue("Аня", out int v)) { /* v == 25 */ }

// HashSet<T> — множество уникальных значений
var set = new HashSet<int> { 1, 2, 2, 3 }; // {1, 2, 3}
set.Add(2);                   // false — уже есть
set.Contains(3);              // true

Методы

Параметры, перегрузка, ref/out, опциональные аргументы и лямбды.

// Обычный метод: тип возврата, имя, параметры
int Add(int a, int b) => a + b;     // => expression body

// Опциональные параметры (со значением по умолчанию)
int Power(int x, int exp = 2) => (int)Math.Pow(x, exp);
Power(5);      // 25 (exp = 2)
Power(2, 3);   // 8

// Именованные аргументы
Power(exp: 3, x: 2); // 8

// Перегрузка — одно имя, разные сигнатуры
string Describe(int n) => $"число {n}";
string Describe(string s) => $"строка {s}";

// ref — передать по ссылке (метод меняет переменную вызывающего)
void Double(ref int x) => x *= 2;
int y = 5; Double(ref y);   // y == 10

// out — вернуть несколько значений
void MinMax(int[] a, out int min, out int max)
{
    min = a.Min(); max = a.Max();
}
MinMax(new[] { 3, 1, 9 }, out int lo, out int hi); // lo=1, hi=9

// params — переменное число аргументов
int Sum(params int[] xs) => xs.Sum();
Sum(1, 2, 3, 4);  // 10

// Лямбды (анонимные функции)
Func<int, int> square = x => x * x;       // принимает и возвращает
square(4);                                // 16
Action<string> log = msg => Console.WriteLine(msg); // ничего не возвращает

Классы и ООП

Поля, свойства (property), конструктор и инкапсуляция.

class Person
{
    // Приватное поле (инкапсуляция: скрыто от внешнего кода)
    private int _age;

    // Авто-свойство: компилятор сам создаёт скрытое поле
    public string Name { get; set; }

    // Свойство с логикой в set
    public int Age
    {
        get => _age;
        set => _age = value < 0 ? 0 : value; // защита от отрицательных
    }

    // Свойство только для чтения (вычисляемое)
    public bool IsAdult => _age >= 18;

    // Конструктор — инициализация при создании объекта
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    // Метод
    public string Greet() => $"Я {Name}, мне {Age}";
}

// Использование:
var p = new Person("Аня", 25);
p.Name;        // "Аня"
p.Greet();     // "Я Аня, мне 25"
p.IsAdult;     // true
p.Age = -5;    // сеттер защитит -> Age == 0

// Object initializer — задать свойства при создании
var p2 = new Person("Боб", 30) { Name = "Боря" };

Наследование и интерфейсы

// Базовый класс
class Animal
{
    public string Name { get; set; }
    // virtual — метод можно переопределить в наследниках
    public virtual string Sound() => "...";
}

// Наследник : базовый класс
class Dog : Animal
{
    // override — переопределяем virtual-метод
    public override string Sound() => "Гав";
}

Animal a = new Dog { Name = "Рекс" };
a.Sound();  // "Гав" — полиморфизм (вызвался метод Dog)

// abstract — нельзя создать экземпляр, только наследовать
abstract class Shape
{
    public abstract double Area(); // без тела — обязан реализовать наследник
}
class Circle : Shape
{
    public double R;
    public override double Area() => Math.PI * R * R;
}

// Интерфейс — контракт (что класс умеет, без реализации)
interface IMovable
{
    void Move(int dx, int dy); // только сигнатура
}
class Player : IMovable        // класс обязуется реализовать Move
{
    public int X, Y;
    public void Move(int dx, int dy) { X += dx; Y += dy; }
}
// Класс может реализовать несколько интерфейсов (в отличие от наследования)

Записи и структуры

// record — неизменяемый класс с авто-равенством (C# 9+)
record Point(int X, int Y);

var p1 = new Point(1, 2);
var p2 = new Point(1, 2);
p1 == p2;                  // true — сравнение по значениям, не по ссылке!
var p3 = p1 with { Y = 9 }; // with — копия с изменением: Point(1, 9)

// struct — значимый тип (копируется целиком, лежит в стеке)
struct Vector
{
    public double X, Y;
    public double Length => Math.Sqrt(X * X + Y * Y);
}
var v = new Vector { X = 3, Y = 4 };
v.Length;  // 5

// enum — перечисление именованных констант
enum Status { Active, Paused, Done } // 0, 1, 2
Status st = Status.Active;
if (st == Status.Active) { /* ... */ }
(int)Status.Done;          // 2 — приведение к числу

Исключения

try
{
    int x = int.Parse("не число"); // бросит FormatException
}
catch (FormatException ex)        // ловим конкретный тип
{
    Console.WriteLine($"Ошибка формата: {ex.Message}");
}
catch (Exception ex)              // ловим всё остальное
{
    Console.WriteLine($"Прочее: {ex.Message}");
}
finally
{
    // выполнится ВСЕГДА (и при ошибке, и без): закрыть файл и т.п.
    Console.WriteLine("finally");
}

// Бросить исключение самому
int Divide(int a, int b)
{
    if (b == 0)
        throw new DivideByZeroException("делить на ноль нельзя");
    return a / b;
}

// Своё исключение
class MyException : Exception
{
    public MyException(string msg) : base(msg) { }
}

LINQ

Запросы к коллекциям: фильтрация, проекция, сортировка, группировка.

using System.Linq;

var nums = new[] { 5, 3, 8, 1, 9, 2 };

// Where — фильтр (оставить подходящие)
var big = nums.Where(x => x > 4);          // 5, 8, 9

// Select — проекция (преобразовать каждый элемент)
var squares = nums.Select(x => x * x);     // 25, 9, 64, ...

// OrderBy / OrderByDescending — сортировка
var sorted = nums.OrderBy(x => x);         // 1, 2, 3, 5, 8, 9

// Цепочки + материализация
var top = nums.Where(x => x > 3)
              .OrderByDescending(x => x)
              .Take(2)
              .ToList();                    // [9, 8]

// Агрегаты
nums.Sum();      // 28
nums.Max();      // 9
nums.Average();  // 4.66...
nums.Count(x => x % 2 == 0); // 3 — сколько чётных
nums.Any(x => x > 8);        // true
nums.First(x => x > 4);      // 5

// GroupBy — группировка
var words = new[] { "кот", "корова", "пёс", "пони" };
var groups = words.GroupBy(w => w[0]);     // по первой букве
foreach (var g in groups)
    Console.WriteLine($"{g.Key}: {g.Count()}"); // к: 2, п: 2

// Альтернативный синтаксис запроса
var q = from x in nums where x > 3 orderby x select x;

async/await и обобщения

Дженерики (<T>) для типобезопасного переиспользования и асинхронность.

using System.Threading.Tasks;

// Обобщённый метод: T — любой тип, определяется при вызове
T First<T>(T[] items) => items[0];
First(new[] { 10, 20 });       // 10  (T = int)
First(new[] { "a", "b" });     // "a" (T = string)

// Обобщённый класс
class Box<T>
{
    public T Value { get; set; }
    public Box(T value) => Value = value;
}
var box = new Box<int>(42);
box.Value;                     // 42

// Ограничение типа: where T : ... (T должен наследовать/реализовать)
T Max<T>(T a, T b) where T : IComparable<T>
    => a.CompareTo(b) > 0 ? a : b;
Max(3, 7);                     // 7

// async / await — асинхронность без блокировки потока
async Task<int> LoadAsync()
{
    await Task.Delay(100);     // ждём, не блокируя поток
    return 42;                 // результат оборачивается в Task<int>
}

async Task UseAsync()
{
    int result = await LoadAsync(); // await разворачивает Task<int> -> int
    Console.WriteLine(result);      // 42
}

// Task — операция без результата; Task<T> — с результатом T
// Несколько задач параллельно:
// await Task.WhenAll(LoadAsync(), LoadAsync());
Поддержать проект