Три фундаментальных принципа объектно-ориентированного программирования (ООП), на которых строится архитектура приложений в C#.
1. Инкапсуляция
Это механизм скрытия внутреннего состояния и реализации объекта от внешнего мира, а также объединение данных (полей) и методов, работающих с этими данными, в единую сущность (класс).
Реализация в C#: Осуществляется с помощью модификаторов доступа (private, protected, internal, public). Состояние класса обычно хранится в private полях, а доступ к ним предоставляется через public методы или свойства (properties) с аксессорами get и set.
Смысл: Защита данных от некорректного изменения извне (например, проверка возраста на отрицательное значение в set-аксессоре) и скрытие сложности реализации.
2. Наследование
Механизм, позволяющий создать новый класс (производный/наследник) на основе существующего (базового/родительского). Наследник перенимает все поля, свойства и методы родителя (кроме приватных конструкторов), может их использовать, изменять или добавлять новые.
Реализация в C#: Обозначается двоеточием: class Derived : Base. В C# поддерживается только множественное наследование интерфейсов, но множественное наследование реализаций (классов) запрещено (один класс может иметь только одного прямого предка). Вызов родительского конструктора или метода осуществляется через ключевое слово base.
Смысл: Исключение дублирования кода (DRY) и выстраивание иерархии “is-a” (является).
3. Полиморфизм
Способность системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Буквально — “множество форм”. Позволяет одному и тому же коду работать с разными типами данных.
Статический полиморфизм (во время компиляции): Перегрузка методов (methods overloading) — создание методов с одинаковым именем, но разной сигнатурой (разным набором параметров).
Динамический полиморфизм (во время выполнения): Переопределение методов (method overriding). В C# родительский класс объявляет метод как virtual (или abstract), а класс-наследник переопределяет его логику с помощью ключевого слова override. При вызове такого метода через ссылку базового типа среда CLR (Common Language Runtime) автоматически определяет фактический тип объекта в памяти и вызывает соответствующую переопределенную реализацию.
2. Классы и объекты в С#
Класс (Class)
Класс в C# — это пользовательский ссылочный тип данных (reference type), представляющий собой чертеж, шаблон или метаданные. Он описывает структуру: какие данные (поля, константы) и какое поведение (методы, свойства, события, конструкторы) будут иметь сущности, созданные по этому шаблону.
Классы в C# не занимают память под данные программы до момента их инстанцирования.
Классы поддерживают наследование и интерфейсы.
Объект (Object / Instance)
Объект — это конкретный экземпляр класса. Если класс — это чертеж дома, то объект — это сам построенный дом.
Память: Так как класс является ссылочным типом, объекты всегда создаются в управляемой куче (Managed Heap). Переменная, в которую сохраняется объект, хранится в стеке (Stack) и содержит лишь ссылку (адрес в памяти) на фактические данные в куче.
Создание: Инстанцирование объекта происходит с помощью оператора new, который выделяет память в куче, обнуляет её, вызывает конструктор класса для инициализации состояния и возвращает ссылку на объект: MyClass obj = new MyClass();.
Жизненный цикл: Разработчик управляет только созданием объектов. Уничтожение объектов и освобождение памяти в C# происходит автоматически с помощью сборщика мусора (Garbage Collector — GC), когда на объект больше не остается активных ссылок.
3. Обработка параметров командной строки в C#
Параметры командной строки — это строковые аргументы, которые передаются программе операционной системой в момент её запуска через консоль/терминал (например, myapp.exe -v output.txt).
В C# существует два основных способа работы с этими параметрами:
1. Через параметр метода Main
Классическая точка входа в программу на C# принимает массив строк:
static void Main(string[] args)
Массив args содержит все переданные аргументы.
Важное отличие от C/C++: В C# нулевой элемент args[0] — это первый переданный аргумент, а не имя исполняемого файла.
Аргументы разделяются пробелами. Если нужно передать строку с пробелами как один аргумент, её заключают в двойные кавычки: myapp.exe "file with spaces.txt".
2. Через класс Environment
В любом месте программы (даже глубоко в других классах, вне метода Main) можно получить доступ к аргументам запуска с помощью метода:
string[] args = Environment.GetCommandLineArgs();
Ключевое отличие: В массиве, возвращаемом этим методом, индекс [0]всегда содержит путь к исполняемому файлу (или имя сборки), а пользовательские аргументы начинаются с индекса [1].
Современный C# (Top-level statements): Начиная с C# 9, при использовании неявной точки входа (top-level statements) массив args доступен “из коробки” прямо в корне файла Program.cs без явного объявления метода Main.
4. Разделение интересов (Separation of Concerns, SoC)
Разделение интересов (SoC) — это фундаментальный принцип программной инженерии, направленный на разделение компьютерной программы на независимые секции (модули, слои, классы), каждая из которых отвечает за отдельную, специфическую задачу (интерес).
Суть принципа в C#:
Бизнес-логика (расчеты, правила предметной области) не должна смешиваться с логикой работы с базой данных (SQL-запросы, Entity Framework) или с логикой отображения (UI, кнопки, HTTP-ответы).
Это достигается за счет создания разных пространств имен (namespaces), изолированных сборок (Class Library projects) и применения интерфейсов (interfaces) для абстрагирования слоев друг от друга.
Реализация на практике:
Паттерны архитектуры: MVC (Model-View-Controller), MVP, MVVM. В MVC:
Model — отвечает только за данные и бизнес-логику.
View — отвечает только за отображение.
Controller — отвечает за маршрутизацию и связывание.
Связь с SOLID: Принцип SoC тесно связан с буквой S из SOLID — Single Responsibility Principle (Принцип единственной ответственности). Класс должен иметь только одну причину для изменения.
Преимущества: Высокая тестируемость (можно написать Unit-тесты для логики, не поднимая базу данных), легкость поддержки, возможность параллельной работы команд (одни делают UI, другие — ядро), низкая связность (loose coupling).
5. Ввод и вывод информации. Форматирование строк
Ввод/Вывод в консоли:
В C# консольный I/O осуществляется через статический класс System.Console.
Вывод:
Console.Write() — выводит текст без перехода на новую строку.
Console.WriteLine() — выводит текст и добавляет символ переноса строки (\r\n в Windows).
Ввод:
Console.ReadLine() — читает строку, введенную пользователем, до нажатия Enter. Возвращает тип string (или string?).
Console.ReadKey() — считывает одно нажатие клавиши (возвращает структуру ConsoleKeyInfo).
Console.Read() — считывает следующий символ из стандартного потока ввода и возвращает его ASCII/Unicode код (тип int).
Форматирование строк:
C# предлагает три основных способа интеграции переменных в текст и их форматирования:
Композитное форматирование (String.Format):
Использует индексы в фигурных скобках.
Console.WriteLine("Имя: {0}, Возраст: {1}", name, age);string res = string.Format("Цена: {0:C}", price);
Строковая интерполяция (String Interpolation) (начиная с C# 6.0):
Перед строкой ставится символ $. Самый современный и читаемый подход. Выражения пишутся прямо внутри фигурных скобок.
Console.WriteLine($"Имя: {name}, Возраст: {age}");
Спецификаторы формата:
Используются после двоеточия для указания типа вывода. Работают как в string.Format, так и в интерполяции.
(Currency) — форматирует число как валюту (в зависимости от локали ОС, например $10.00 или 10,00 ₽).
{:F2} (Fixed-point) — число с фиксированным количеством знаков после запятой (например, 3.14).
{:D5} (Decimal) — целое число с добивкой нулями слева (например, 00042).
(Hexadecimal) — шестнадцатеричный формат.
— пользовательское форматирование даты.
6. Методы
Метод — это именованный блок кода, содержащий последовательность инструкций для выполнения определенной задачи. В C# все методы должны быть частью класса или структуры (в C# нет глобальных функций, как в C++).
Структура объявления метода:[модификатор_доступа] [модификатор] <возвращаемый_тип> <ИмяМетода>([параметры]) { // тело }
Модификаторы:public, private, static (метод принадлежит классу, а не объекту), virtual, async и др.
Возвращаемый тип: Любой тип данных C# или void, если метод ничего не возвращает. Для void оператор return можно опустить или использовать просто return; для выхода из метода.
Передача параметров в методы:
По значению (по умолчанию): В метод копируется значение переменной (для значимых типов) или копия ссылки (для ссылочных). Изменение самого параметра внутри метода не затрагивает исходную переменную.
По ссылке (ref): В метод передается физический адрес переменной. Требует обязательной инициализации переменной до её передачи в метод. Любые изменения внутри метода отражаются на исходной переменной.
Выходной параметр (out): Как и ref, передает по ссылке, но переменная может быть не инициализирована до вызова. Метод обязан присвоить значение параметру out до выхода. Используется для возврата нескольких значений (например, int.TryParse).
Входной параметр (in): Передача по ссылке, но с запретом на изменение внутри метода (read-only reference). Используется для оптимизации производительности при передаче больших структур (struct).
Дополнительные возможности:
Необязательные параметры: Позволяют задать дефолтное значение (void Do(int x = 5)). Должны идти в конце списка параметров.
Именованные аргументы: При вызове метода аргументы можно передавать не по порядку, а указывая имя параметра: Do(age: 25, name: "Ivan").
Expression-bodied методы: Сокращенный синтаксис (через =>) для методов из одной строки: public int Add(int a, int b) => a + b;.
Параметр params: Позволяет передавать в метод переменное количество аргументов, которые внутри упаковываются в массив (void Log(params string[] msgs)).
7. Циклы: for, foreach/in
В C# циклы используются для многократного выполнения блока кода.
1. Цикл for
Классический цикл со счетчиком. Выполняется до тех пор, пока истинно заданное условие.
Синтаксис:
for (инициализация; условие; итерация) { // тело цикла }
Инициализация: выполняется один раз перед стартом (например, int i = 0).
Условие: проверяется перед каждой итерацией. Если true — цикл выполняется, если false — прерывается (например, i < 10).
Итерация: выполняется после каждого прохода тела цикла (например, i++).
Особенности: Все три блока в заголовке for опциональны (можно написать for(;;), получив бесконечный цикл). В for можно объявлять сразу несколько переменных (for (int i=0, j=10; i < j; i++, j--)).
2. Цикл foreach / in
Цикл, специально разработанный для перебора элементов массивов и любых коллекций (списков, словарей и т.д.).
Синтаксис:
foreach (Тип элемент in коллекция) { // действия с элементом }
Принцип работы (под капотом): Компилятор разворачивает foreach в конструкцию while, которая использует интерфейсы IEnumerable и IEnumerator. Сначала вызывается метод GetEnumerator(), а затем в цикле проверяется MoveNext() и извлекается свойство Current.
Ограничения (Важно!):
Итерационная переменная (элемент) доступна только для чтения (read-only). Нельзя изменить сам элемент коллекции, написав элемент = новое_значение; (для структур и примитивов).
Нельзя изменять структуру самой коллекции (добавлять или удалять элементы) внутри цикла foreach. Если попытаться это сделать (например, list.Remove(item)), будет выброшено исключение InvalidOperationException (коллекция была изменена).
8. Циклы: while, do/while
Циклы while и do/while используются для выполнения блока кода до тех пор, пока заданное логическое условие остается истинным. Они оптимальны в ситуациях, когда точное количество итераций заранее неизвестно и зависит от динамических данных (например, ввод пользователя, чтение из файла до конца).
1. Цикл while (цикл с предусловием)
Проверяет условие до выполнения тела цикла. Если условие ложно изначально, тело цикла не выполнится ни одного раза.
Синтаксис:
while (условие) { // тело цикла}
2. Цикл do/while (цикл с постусловием)
Проверяет условие после выполнения тела цикла. Главная особенность: тело цикла гарантированно выполнится хотя бы один раз, независимо от истинности условия.
Синтаксис:
do{ // тело цикла} while (условие); // Обязательна точка с запятой в конце!
9. Условие if…else. Конструкция switch.
Это основные конструкции ветвления в C#, управляющие потоком выполнения программы.
1. Конструкция if...else
Позволяет выполнить блок кода в зависимости от истинности логического (булева) выражения. Выражение обязательно должно возвращать тип bool (в отличие от C++, где можно передать int и 0 расценивается как false).
Синтаксис:
if (условие1) { // выполнится, если условие1 == true} else if (условие2) { // выполнится, если условие1 == false, а условие2 == true} else { // выполнится, если все предыдущие условия ложны}
2. Конструкция switch
Механизм множественного выбора, сравнивающий значение выражения (паттерн) с набором констант (меток case).
Ключевые особенности switch в C#:
В отличие от C/C++, в C# запрещено “проваливание” (fall-through) между непустыми блоками case. Каждый блок с кодом обязан завершаться оператором безусловного перехода: break, return, goto или throw.
Проваливание допускается, только если блок case полностью пуст (полезно для группировки условий).
С версии C# 7.0 конструкция поддерживает сопоставление с образцом (Pattern Matching), что позволяет проверять не только точное совпадение значений, но и типы данных (например, case int i when i > 0:).
С версии C# 8.0 доступны выражения switch (switch expressions), которые позволяют писать более компактный код, сразу возвращающий значение (например, var res = x switch { 1 => "Один", _ => "Другое" };).
10. Поразрядный оператор И
Поразрядное (битовое) И (AND) обозначается символом &.
Выполняет логическую операцию И над каждой соответствующей парой битов двух целочисленных операндов.
Таблица истинности:
Результат равен 1 только в том случае, если оба соответствующих бита операндов равны 1. В остальных случаях (1&0, 0&1, 0&0) результат равен 0.
Практическое применение в программировании:
Применяется для маскирования битов (извлечения или проверки состояния конкретного бита).
Пример: Чтобы проверить, установлен ли третий бит в числе, мы применяем маску: if ((number & 4) == 4).
(Примечание: оператор & в C# также может применяться к типам bool. В отличие от логического &&, он не выполняет сокращенное (short-circuit) вычисление, а гарантированно вычисляет оба операнда).
11. Поразрядный оператор ИЛИ
Поразрядное (битовое) ИЛИ (OR) обозначается символом |.
Выполняет логическую операцию ИЛИ над каждой парой битов.
Таблица истинности:
Результат равен 0 только в том случае, если оба бита равны 0. Если хотя бы один из битов равен 1, результат будет 1.
Практическое применение в программировании:
Применяется для установки (включения) битов или комбинирования флагов (битовых масок).
Пример: Использование перечислений с атрибутом [Flags]. var options = Option.Read | Option.Write; устанавливает в переменной одновременно и бит чтения, и бит записи.
12. Поразрядный оператор исключающее ИЛИ
Поразрядное исключающее ИЛИ (XOR) обозначается символом ^.
Таблица истинности:
Результат равен 1, если соответствующие биты операндов различны (1^0 = 1, 0^1 = 1).
Результат равен 0, если биты одинаковы (1^1 = 0, 0^0 = 0).
Практическое применение в программировании:
Инвертирование (переключение) конкретных битов:x ^ mask переключит те биты в числе x, где в маске стоят единицы.
Свойство обратимости (a ^ b ^ b = a) используется в простейшем криптографическом шифровании и вычислении контрольных сумм.
Обнуление регистра: a ^ a всегда равно 0.
Обмен значениями двух целочисленных переменных без использования временной памяти (Swap): a ^= b; b ^= a; a ^= b;.
13. Поразрядный оператор НЕ
Поразрядное (битовое) НЕ (NOT) обозначается символом ~ (тильда).
В отличие от предыдущих, это унарный оператор (применяется к одному числу).
Таблица истинности:
Инвертирует каждый бит числа: заменяет все нули на единицы, а все единицы на нули (побитовое дополнение).
Практическое применение в программировании:
В математике дополнительных кодов инверсия тесно связана со сменой знака числа: -x = ~x + 1.
Создание обратных масок для сброса (выключения) битов.
Пример: Чтобы сбросить (установить в 0) третий бит числа (маска 4, бинарно 00000100), не затронув остальные, используют конструкцию x = x & ~4;. Тильда инвертирует маску в 11111011, а оператор & “пропускает” все биты, кроме третьего, который обнуляется.
14. Простые массивы
Определение:
Простой (одномерный) массив — это структура данных, хранящая фиксированное количество элементов одного типа в непрерывном блоке памяти. В C# массивы являются ссылочными типами (Reference Types) и неявно наследуются от базового абстрактного класса System.Array.
Особенности:
Индексация: Начинается с 0. Индекс последнего элемента равен Length - 1. Обращение к несуществующему индексу вызывает исключение времени выполнения IndexOutOfRangeException.
Память: Поскольку массив — ссылочный тип, переменная-массив (в стеке) хранит лишь ссылку на область памяти в куче (Managed Heap), где физически располагаются сами элементы.
Создание: Размер массива задается при инстанцировании оператором new и в дальнейшем изменить его нельзя (в отличие от List<T>).
int[] numbers = new int[5]; // массив из 5 целых чисел (инициализируются нулями)string[] names = { "Alice", "Bob" }; // синтаксический сахар для инициализации
Современный C#: Доступны индексы от конца (numbers[^1] — последний элемент) и диапазоны (ranges, numbers[1..3]).
15. Прямоугольный массив
Определение:
Прямоугольный массив в C# — это истинно многомерный массив, элементы которого организованы в виде матрицы (решетки). Характерная черта: каждая строка (измерение) имеет строго одинаковую длину.
Синтаксис и особенности:
Объявление: Измерения разделяются запятой внутри одних квадратных скобок [,].
int[,] matrix = new int[3, 4]; // Матрица 3 строки на 4 столбца
Инициализация:
int[,] matrix2 = { {1, 2}, {3, 4}, {5, 6} };
Доступ к элементам: Осуществляется через одну пару скобок с указанием индексов через запятую: matrix[0, 1] = 5;.
Метаданные: Свойство Length возвращает общее количество элементов (в примере выше — 12). Чтобы узнать длину конкретного измерения, используется метод GetLength(int dimension). Например, matrix.GetLength(0) вернет 3 (число строк), а matrix.GetLength(1) вернет 4 (число столбцов).
В памяти: Физически все элементы прямоугольного массива располагаются в куче как единый непрерывный блок памяти. Вычисление адреса нужного элемента происходит математически через смещение, что делает обращение к элементам очень быстрым.
16. Ломаный массив (Зубчатый массив)
Определение:
Ломаный (или зубчатый) массив в C# (Jagged Array) — это массив массивов. В отличие от прямоугольного массива, где каждая строка имеет одинаковую длину, в ломаном массиве каждая строка (внутренний массив) является самостоятельным объектом и может иметь совершенно разную длину.
Синтаксис и особенности:
Объявление: Используются двойные квадратные скобки [][].
int[][] jagged = new int[3][]; // Массив из 3-х ссылок на другие массивы
Инициализация: В отличие от прямоугольного массива, ломаный массив нельзя инициализировать одним действием (если не использовать явный синтаксис инициализации элементов). Необходимо выделить память под каждый внутренний массив отдельно:
jagged[0] = new int[5]; // Первая строка: 5 элементовjagged[1] = new int[2]; // Вторая строка: 2 элементаjagged[2] = new int[8]; // Третья строка: 8 элементов
Доступ к элементам: Осуществляется через двойные скобки: jagged[0][1] = 42;. Первая скобка обращается к внешнему массиву и возвращает ссылку на внутренний массив, вторая — обращается к элементу этого внутреннего массива.
Организация в памяти: Ломаный массив не является единым непрерывным блоком памяти. В куче создается один “главный” массив, который хранит только ссылки на другие массивы. Сами внутренние массивы разбросаны по куче (Managed Heap) независимо друг от друга. Это снижает производительность из-за двойного разыменования указателей (pointer chasing) и нагрузки на сборщик мусора (GC), но позволяет существенно экономить память, если данные сильно разрежены.
17. Перечисление в C# (enum)
Определение:
Перечисление (enum) — это значимый тип данных (Value Type), который определяет набор строго типизированных именованных целочисленных констант. Все перечисления в C# неявно наследуются от базового класса System.Enum.
Назначение:
Перечисления используются для повышения читаемости кода (вместо использования “магических чисел”), обеспечения строгой типизации и защиты от передачи некорректных значений в методы.
Особенности и синтаксис:
Базовый тип: По умолчанию базовым типом для элементов перечисления является int, а отсчет начинается с 0.
Преобразование типов (Casting): Так как enum основан на числах, его можно явно приводить к числу и обратно: int val = (int)Days.Mon;.
Битовые флаги ([Flags]): Если применить к перечислению атрибут [Flags] и задать константам значения степеней двойки (1, 2, 4, 8…), то значения enum можно будет комбинировать с помощью поразрядного ИЛИ (|) и проверять с помощью поразрядного И (&) (или встроенного метода .HasFlag()). Это позволяет хранить множество состояний в одной переменной.
18. Структуры (struct)
Определение:
Структура (struct) в C# — это пользовательский значимый тип данных (Value Type), предназначенный для создания легковесных объектов, объединяющих логически связанные данные.
Ключевые отличия структур от классов (class):
Хранение в памяти: Поскольку структура является Value Type, экземпляры структур выделяются в стеке (Stack), если они объявлены как локальные переменные, либо встраиваются (inlined) непосредственно в объект, если они являются полями класса. Это избавляет от накладных расходов на выделение памяти в куче и работу сборщика мусора (GC).
Семантика присваивания: При присваивании одной переменной-структуры другой (structA = structB) или при передаче структуры в метод по значению, происходит полное побитовое копирование всех её данных. Изменение копии никак не влияет на оригинал (в отличие от классов, где копируется только ссылка).
Наследование: Структуры неявно наследуются от System.ValueType. Они не поддерживают наследование от других структур или классов (они неявно sealed), и от них нельзя унаследоваться. Однако структуры могут реализовывать интерфейсы (interface).
Сборка мусора: Структуры в стеке уничтожаются мгновенно и детерминированно при выходе из области видимости (выходе из метода).
Когда использовать:
Структуры рекомендуется применять для маленьких, логически неделимых и желательно неизменяемых (immutable) наборов данных, размер которых не превышает 16-24 байт (например: DateTime, Point, Color, Vector3). Если размер структуры большой, постоянное копирование значений при передаче в методы приведет к падению производительности (если не использовать модификаторы ref или in).
19. Пространства имен (namespace)
Определение:
Пространство имен (namespace) — это механизм логической группировки связанных классов, структур, интерфейсов, перечислений и делегатов. Он предотвращает конфликты имен (naming collisions) в крупных проектах или при подключении сторонних библиотек.
Синтаксис и особенности:
Объявление:
namespace Company.Project.Network{ class Client { }}
Доступ к типам: Чтобы использовать класс Client извне его пространства имен, необходимо либо указать его полное имя (Company.Project.Network.Client), либо импортировать пространство имен с помощью директивы using в начале файла: using Company.Project.Network;.
Вложенность: Пространства имен могут быть вложенными друг в друга как физически (один блок внутри другого), так и через точку в названии (как показано выше).
Алиасы (Псевдонимы): Директива using позволяет создавать псевдонимы для длинных типов или разрешения конфликтов (если в двух namespace есть класс с одинаковым именем): using NetClient = Company.Project.Network.Client;.
Современный C# (Начиная с версии 10.0):
File-scoped namespaces: Позволяет объявить пространство имен на весь файл без фигурных скобок namespace Company.Project;, что экономит отступы (indentation).
Global using: Директива global using System; позволяет импортировать пространство имен один раз на весь проект, чтобы не писать его в каждом отдельном файле.
20. Области видимости и Модификаторы доступа
Область видимости (Scope) — это часть программы (блок кода), в пределах которой переменная или тип доступны для использования. В C# области видимости строго иерархичны и определяются фигурными скобками {} (уровень пространства имен, уровень класса, уровень метода, уровень блока if/while). Переменная, объявленная во внутреннем блоке, невидима снаружи.
На уровне типов (классов/структур) и их членов (полей/методов) область видимости жестко контролируется Модификаторами доступа (Access Modifiers):
private: Доступен только внутри текущего класса или структуры. Это модификатор по умолчанию для всех членов класса (если не указано иное). Обеспечивает строгую инкапсуляцию.
public: Отсутствие ограничений. Доступен из любого места программы и из других сборок (dll), ссылающихся на текущую.
protected: Доступен внутри текущего класса, а также во всех классах-наследниках (даже если они находятся в другой сборке).
internal: Доступен из любого места, но только в пределах текущей сборки (Assembly/Проекта). Это модификатор по умолчанию для самих классов (верхнего уровня).
protected internal: Доступен текущей сборке ИЛИ в классах-наследниках в других сборках (комбинация “ИЛИ”).
private protected: (Введено в C# 7.2). Доступен только классам-наследникам, которые находятся строго в той же сборке (комбинация “И”).
21. Перегрузка методов (Method Overloading)
Определение:
Перегрузка методов — это механизм (реализация статического полиморфизма), позволяющий объявлять в одном классе несколько методов с одинаковым именем, но с разной сигнатурой.
Сигнатура метода в C# включает в себя:
Имя метода.
Количество параметров.
Типы параметров.
Порядок следования параметров.
Модификаторы параметров (ref, out, in).
Правила перегрузки:
Компилятор разрешает вызов перегруженного метода на этапе компиляции (Early Binding), анализируя аргументы, переданные при вызове.
Внимание:Возвращаемый тип метода не является частью сигнатуры. Вы не можете создать два метода с одинаковыми параметрами, отличающиеся только тем, что один возвращает int, а другой double.
Наличие модификаторов ref или out делает сигнатуру отличной от метода без модификаторов. Однако нельзя перегрузить методы, отличающиеся только тем, что в одном стоит ref, а в другом out.
Модификатор params не влияет на разрешение перегрузки.
Назначение:
Делает интерфейс класса более интуитивно понятным (например, метод Console.WriteLine() имеет 18 перегрузок, принимающих string, int, bool, double и т.д., что избавляет программиста от необходимости запоминать методы вроде WriteLineInt(), WriteLineString()).
22. Указатель this
Определение:
Ключевое слово this — это зарезервированная неявная ссылка, доступная внутри нестатических методов, свойств и конструкторов класса (или структуры). Она указывает на текущий экземпляр объекта, для которого был вызван этот метод.
Практическое применение this в C#:
Разрешение неоднозначности имен (Shadowing): Если имя параметра конструктора совпадает с именем поля класса, this помогает компилятору понять, где поле, а где аргумент:
public Person(string name) { this.name = name; // this.name - поле объекта, name - параметр метода}
Передача текущего объекта в другие методы:EventBus.Register(this);
Вызов других конструкторов того же класса (Constructor Chaining): Позволяет избежать дублирования кода инициализации.
public Person() : this("Unknown") { } // Вызовет конструктор с параметром string
Создание Индексаторов (Indexers): Позволяет обращаться к объекту как к массиву: public int this[int index] { get { return arr[index]; } }.
Объявление методов расширения (Extension Methods): Модификатор this перед первым параметром статического метода указывает компилятору, что метод “расширяет” указанный тип. public static void Print(this string str) { ... }.
Ограничение:
Слово this недоступно в static методах и свойствах, так как статические члены принадлежат самому типу (классу), а не конкретному экземпляру объекта, и физически не привязаны ни к какому участку памяти с данными объекта.