Предыдущий набор вопросов посмотрело большое количество человек.
В продолжение темы решил рассмотреть новую группу вопросов, опубликованную в статье.
Вопросы условно разделены по категориям сложности: с 1 по 53 предназначены для начинающих, с 54 по 121 — для опытных, оставшиеся — для экспертов.
Везде, включая оригинал на украинском, вопросы с 91 по 99 отсутствуют.
К сожалению, статья содержит только вопросы, искать ответы пришлось по одному с использованием Яндекс Нейро.
Также часть ответов нашел в данном Telegram-канале.
Ответы максимально сократил, убрав детали. Предпочтение старался отдавать документации Микрософт.
Практические задания пропущены. 😝
Warning
Яндекс предупреждает: ответы сформированы YandexGPT на основе текстов выбранных сайтов. В них могут быть неточности.
Буду рад, если кому помог с подготовкой к собеседованию!
1. Назовите основные принципы ООП.
- Инкапсуляция. Скрытие деталей реализации объекта и предоставление только интерфейса для взаимодействия с ним. Это позволяет изолировать изменения в одной части программы от других частей, что делает код более надёжным и устойчивым к изменениям.
- Наследование. Создание новых классов на основе существующих. Это способствует повторному использованию кода и созданию иерархий классов. Наследование позволяет наследникам использовать свойства и методы предков и переопределять или расширять их, если это необходимо.
- Полиморфизм. Способность объектов разных классов обладать общим интерфейсом. Это позволяет обрабатывать объекты разных типов с помощью общих методов и функций. Полиморфизм делает код более гибким и расширяемым.
- Абстракция. Процесс выделения общих характеристик объектов и создание абстрактных классов или интерфейсов для их представления. Абстракция помогает упростить модель системы, делая её более понятной и управляемой.
2. От какого класса неявно наследуются все классы в .NET? Разрешено ли множественное наследование в C#?
Все классы в .NET неявно наследуются от класса Object или его производного типа.
В C# и .NET поддерживается только одиночное наследование. Это означает, что каждый класс может наследовать члены только одного класса.
3. Что такое рекурсия?
В C# рекурсия — это процесс, в ходе которого метод вызывает самого себя. Метод, вызывающий самого себя, называют рекурсивным.
4. Что такое лямбда-выражение?
Лямбда-выражение — это другой способ создания анонимной функции, обычно служит для работы с делегатами и событиями.
x => x / 2; //одиночное
x => { return x / 2; } //блочное5. Что такое параллельное программирование (многопоточность) и его назначение? Какие классы используются?
В прошлом для распараллеливания требовались низкоуровневые манипуляции с потоками и блокировками. Библиотека параллельных задач (TPL) упрощает процесс добавления параллелизма в приложение.
Для распараллеливания можно использовать классыTaskиParallel.
6. Что такое JSON?
JavaScript Object Notation — стандартный текстовый формат для хранения и передачи структурированных данных. Он основан на синтаксисе объекта в JavaScript.
7. Как вы понимаете REST?
Это архитектурный стиль взаимодействия компонентов распределённого приложения в сети. Принципы REST:
- Разделение потребностей: клиент управляет пользовательским интерфейсом, а сервер — ресурсами и данными.
- Бесстатусная коммуникация: запрос клиента содержит всю необходимую информацию для его обработки.
- Кэшируемые ресурсы: Ответы сервера могут быть помечены как кэшируемые или некэшируемые.
- Унифицированный интерфейс: Согласованный набор правил с помощью методов HTTP.
8. Расскажите о SPA concept.
Как работает SPA (одностраничное приложение): браузер загружает сразу весь код приложения. Но показывает только конкретный модуль — часть сайта, которая нужна пользователю. Когда пользователь переходит в другую часть приложения, браузер берёт уже загруженные данные и показывает их. И, если нужно, динамически подгружает с сервера нужный контент без обновления страницы.
9. Какие GoF-паттерны использовали?
- Наблюдатель (Observer): позволяет объектам-наблюдателям получать оповещения об изменении состояния других объектов.
- Декоратор (Decorator): позволял динамически добавлять функциональность к объекту без изменения его класса.
- Адаптер (Adapter): преобразует интерфейс одного класса в интерфейс другого класса.
- Итератор (Iterator): предоставляет методы для доступа к элементам коллекции.
- Фасад (Facade): создает упрощённый интерфейс для сложной системы.
- Компоновщик (Composite): создает иерархические древовидные структуры объектов.
- Прототип (Prototype): создает новые объекты путём клонирования существующих.
- Цепочка обязанностей (Chain of Responsibility): строит цепочку объектов, которые могут обрабатывать запросы последовательно.
- Состояние (State): определяет различное поведение объекта в зависимости от его текущего состояния.
- Посетитель (Visitor): добавляет новые операции к классам, не изменяя их исходного кода.
- Мост (Bridge): разделяет абстракцию и реализацию, чтобы они могли изменяться независимо друг от друга.
- Легковес (Flyweight): оптимизирует работу с большим количеством мелких объектов, которые могли быть разделены на общие и уникальные части.
- Прокси (Proxy): Создает объект-заместитель, который мог контролировать доступ к другому объекту.
- Команда (Command): инкапсулирует запрос в виде объекта, что позволяло отделить источник запроса от его исполнения.
- Интерпретатор (Interpreter): определяет грамматику языка и создает интерпретатор для выполнения заданных операций.
- Снимок (Memento): сохраняет состояние объекта и восстанавливал его в будущем.
10. Какая разница между GET и POST HTTP методами?
- Цель использования. GET применяется для запроса данных с сервера, а POST — для отправки данных на сервер для обработки.
- Передача данных. GET передаёт параметры в URL, а POST — в теле запроса.
- Безопасность. GET менее безопасен для передачи чувствительной информации, так как данные видны в URL. POST более безопасен, поскольку данные передаются в теле запроса и не видны в URL.
- Кэширование. Результаты GET-запросов могут быть кэшированы браузерами и прокси-серверами. POST-запросы не кэшируются (кроме некоторых специальных случаев).
- Идемпотентность. GET идемпотентен, то есть повторный GET-запрос не приведёт к изменению состояния на сервере. POST-запрос может привести к изменению состояния на сервере (например, создание новой записи или отправка сообщения).
11. Какую проблему решает Docker? Каковы его плюсы и минусы?
Docker — это платформа, которая позволяет упаковать в контейнер приложение со всем окружением и зависимостями, а затем доставить и запустить его в целевой системе. Приложение, упакованное в контейнер, изолируется от операционной системы и других приложений.
- Плюсы: не зависит от инфраструктуры, изолированность каждого контейнера, скорость развёртывания, упрощение администрирования.
- Минусы: высокое потребление ресурсов, проблемы с Windows и macOS, снижение производительности.
12. Чем принципиально отличаются unit-тесты от интеграционных тестов?
Оба типа важны и дополняют друг друга, отличия:
- Фокус. Unit-тесты фокусируются на внутреннем дизайне программного обеспечения, проверяя, что каждый модуль работает как положено. Интеграционные тесты проверяют взаимодействие между разными модулями или компонентами.
- Сложность. Интеграционные тесты обычно более сложные, чем unit-тесты, так как требуют глубокого понимания всей системы и взаимодействий между её компонентами.
- Зависимость. Unit-тесты независимы и могут проводиться в изоляции. Интеграционные тесты зависят от интегрируемых модулей, и перед их выполнением все модули должны быть протестированы и работать правильно.
- Скорость выполнения. Unit-тесты обычно выполняются быстрее, так как они касаются только одного компонента. Интеграционные тесты могут занимать больше времени из-за своей сложности и более реалистичного тестирования поведения программного обеспечения.
13. Что такое Exception?
Исключение (Exception) - это объект, представляющий ошибку или исключительную ситуацию, возникшую во время выполнения программы.
В C#, исключения обычно являются объектами, производными от класса Exception.
14. Для чего служат try, catch, finally? В каком случае может не выполниться блок finally?
- try используется для обозначения блока кода, где может произойти исключение.
- catch предназначен для обработки исключений. finally содержит код, который выполняется всегда, даже если исключение было брошено.
- finally может не выполниться, если произошло критическое исключение, такое как StackOverflowException или ExecutionEngineException.
try
{
//исполняемый код
}
catch (Exception e)
{
//обработка исключения
}
finally
{
//всегда сработает
}15. Что такое call stack? Какие ключевые слова вы знаете?
Call stack (стек вызовов) - это механизм, используемый для отслеживания места выполнения программы. Когда метод вызывается, его контекст (включая локальные переменные и адрес возврата) помещается в вершину стека.
Ключевые слова:
- throw: Используется для явного броска исключения.
- try, catch, finally: Используются для обработки исключений.
- catch (Exception ex) when (condition): Позволяет фильтровать исключения по дополнительному условию.
16. Что такое ASP.NET?
Это технология, созданная Microsoft для создания современных веб-приложений и служб с помощью .NET.
Существует в разных версиях: ASP.NET и ASP.NET Core. А кроме версий, есть еще четыре модели: Web Forms, MVC, Web API, Razor Pages.
17. Какие существуют типы Action filters?
В ASP.NET Core существует пять типов Action filters:
- Фильтры авторизации (Authorization Filter). Используются для предоставления доступа к определённым частям приложения только аутентифицированным и авторизованным пользователям.
- Фильтры ресурсов (Resource Filter). Позволяют выполнять действия, влияющие на весь HTTP-запрос и ответ, например, изменять заголовки ответа или обрабатывать глобальные исключения.
- Фильтры действий (Action Filter). Выполняют код до и после выполнения метода действия.
- Фильтры результатов (Result Filter). Позволяют манипулировать результатом метода действия до и после его выполнения.
- Фильтры исключений (Exception Filter). Обрабатывают исключения, возникающие во время выполнения метода действия.
18. Что такое Web Service?
Web-сервис в .NET — это компонент, предоставляющий набор функций API или веб-методов. Он развёртывается на веб-сервере, публикует методы, ожидает поступления запросов и возвращает результаты.
В ASP.NET Core сервисы устанавливаются в функции ConfigureServices класса Startup и представляют собой обычные классы или интерфейсы.
19. Что такое CLR?
CLR (Common Language Runtime) — общеязыковая исполняющая среда. Выполняет следующие функции:
- Компилирует код приложения на промежуточном языке (CIL или MSIL) во время его исполнения. Этот код не зависит от компьютера и может выполняться на любой платформе, на которой установлена среда CLR.
- Предоставляет сервисы для приложений .NET. Среди них — управление памятью, защита типов, security и обработка исключений.
- Поддерживает межъязыковую совместимость. Это позволяет разработчикам писать код на разных языках .NET (таких как C#, VB.NET и F#) и использовать их в одном приложении.
20. Что такое сборщик мусора (Garbage Collector) на базовом уровне?
Это специальный программный модуль, который периодически определяет, какие из созданных в динамической памяти объектов больше не используются, и освобождает занимаемую ими память.
21. Что такое делегат?
Делегат в .NET — это тип, который представляет ссылки на методы с определённым списком параметров и типом возвращаемого значения. Пример объявления делегата:
public delegate int PerformCalculation(int x, int y);Делегату можно назначить любой метод, соответствующей типу делегата.
Делегаты используются для передачи методов в качестве аргументов к другим методам.
22. Отличается ли Delegate от Action?
Delegate — это базовый класс для всех делегатов, в то время как Action представляет делегат, который не возвращает значение.
Action является более конкретным представлением делегата, представляет некоторое действие, которое ничего не возвращает.
Action<string> greetAction = delegate (string name) { Console.WriteLine($"Hello, {name}!"); };
greetAction("C# Developer");23. Что такое LINQ и для чего используется? Приведите несколько примеров применения LINQ.
LINQ расширяет язык C# за счет добавления выражений запросов, которые похожи на инструкции SQL и могут использоваться для удобного извлечения и обработки данных из массивов, перечислимых классов, документов XML, реляционных баз данных и сторонних источников данных.
Есть два способа выполнения запроса LINQ: отложенное и немедленное.
string[] people = { "Tom", "Bob", "Sam", "Tim", "Tomas", "Bill" };
var selectedPeople = from p in people where p.ToUpper().StartsWith("T") orderby p select p; //синтаксис запроса
var selectedPeople2 = people.Where(p => p.ToUpper().StartsWith("T")).OrderBy(p => p).ToList(); //синтаксис метода, немедленное исполнение
foreach (string s in selectedPeople) { Console.WriteLine(s); } //отложенное исполнение24. Что такое пространство имен (namespace) и зачем это нужно?
Классы и другие типы в .NET заключаются в специальные контейнеры - пространства имен.
Организуют код по логическим блокам (выполняющих определенную задачу), облегчают поиск классов, а также решают проблему уникальности имен (допускаются одинаковые имена классов в разных пространствах имен).
25. Какие типы данных вы знаете?
Встроенные типы:
- целочисленные со знаком (sbyte, short, int, long),
- целочисленные без знака (byte, ushort, uint, ulong),
- с плавающей точкой (float, double, decimal),
- логический (bool),
- символ (char),
- строка (string),
- объект (object).
26. Какие примитивные типы знаете?
Не все встроенные типы являются примитивными. Операции с примитивными типами выполняются очень быстро.
У класса Type есть свойство IsPrimitive, которое возвращает true для следующих типов:
Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
27. Что такое Nullable-тип?
Структура Nullable или T?, представляет все значения своего базового типа значения T (тоже структуры), а также дополнительное значение null.
Например, можно присвоить переменной bool? любое из следующих трех значений: true, false или null.
Двумя основными элементами Nullable структуры являются HasValue и Value свойства.
28. Что такое тип значения, а что такое тип ссылки? Что из этого class, а что struct? В каком участке памяти они хранятся?
- Значимый тип (value type) содержит само значение и обычно хранится в стеке памяти. Примеры: struct, int, char.
- Ссылочный тип (reference type) хранит ссылку на данные и обычно хранится в стеке как ссылка, а сами данные в управляемой куче. Примеры: class, interface.
29. Чем отличаются value от reference type? String - это reference или value?
- Value type: хранит данные напрямую, копируется при передаче по значению и не может быть null.
- Reference type: хранит ссылку, копируется по ссылке и может быть null. String — это reference type.
30. В чем отличие между string builder и string?
Строка (String) — неизменяемый класс, каждая операция конкатенации создает новую строку в памяти.
StringBuilder — это изменяемый класс, предназначенный для эффективной работы с конкатенацией строк. Он хранит изменяемую последовательность символов.
31. Что такое дженерики? Какие проблемы они решают?
Дженерики позволяют создавать классы и методы, откладывающие спецификацию одного или нескольких параметров типа, пока не будет использоваться класс или метод в коде.
Объединяют повторное использование (универсальный код), безопасность типов (тип проверяется компилятором) и эффективность (для типов значений).
var list = new ArrayList(); //не дженерик, хранит данные любого типа
list.Add("Hello");
list.Add(1);
var list2 = new List<string>(); //дженерик, допускает только строки
list2.Add("Hello");32. Что такое boxing / unboxing?
Упаковка (boxing) - это процесс упаковки значимого типа в объект.
Распаковка (unboxing) - это обратный процесс извлечения значения из упакованного объекта.
int i = 123;
object o = i; //boxing
i = (int)o; //unboxing33. Что такое Array, List, HashSet, Dictionary? Приведите примеры использования этих структур данных. Какая сложность операций с ними (поиск, вставка, удаление)?
Array — это набор элементов фиксированного размера.
List — это динамический массив, который автоматически расширяется при добавлении элементов.
HashSet - это неупорядоченная коллекция уникальных элементов.
Dictionary - словарь хранит объекты, которые представляют пару ключ-значение.
var ar = new int[5] { 1, 2, 3, 4, 5 }; //array
ar[1] = 0; //можно менять, но нельзя добавлять и удалять
Console.WriteLine(Array.IndexOf(ar, 0)); //поиск, O(n)
var list = new List<int>() { 1, 2, 3, 4, 5 }; //list
list.Add(6); //вставка, O(1), в худшем случае O(n)
list.RemoveAt(1); //удаление, O(1)
Console.WriteLine(list.IndexOf(5)); //поиск, O(n)
var hash = new HashSet<int>() { 1, 2, 3, 4, 5}; //hashset
hash.Add(5); //вставка, ничего не добавит, такой элемент уже есть, O(1)
hash.Remove(5); //удаление, O(1)
Console.WriteLine(hash.Contains(5)); //поиск, O(1)
var dict = new Dictionary<int, bool>(); //dictionary
dict.Add(1, true); //вставка, O(1)
dict[2] = true; //вставка/изменение
dict.Remove(1); //удаление, O(1)
Console.WriteLine(dict.ContainsKey(1)); //поиск, O(1)34. Какие знаете коллекции?
System.Collections.Generic содержит следующие обобщенные коллекции:
- List: класс, представляющий последовательный список.
- Dictionary<TKey, TValue>: класс коллекции, хранящей наборы пар "ключ-значение".
- LinkedList: класс двухсвязного списка.
- Queue: класс очереди объектов, работающей по алгоритму FIFO("первый вошел - первый вышел").
- SortedSet: класс отсортированной коллекции однотипных объектов.
- SortedList<TKey, TValue>: класс коллекции, хранящей наборы пар "ключ-значение", отсортированных по ключу.
- SortedDictionary<TKey, TValue>: тоже самое, но имеет другую реализацию в виде дерева.
- Stack: класс стека однотипных объектов.
35. Что делает оператор yield?
yield используется для создания итераторов, позволяя поочередно возвращать значения из метода без прерывания его выполнения.
IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers)
{
foreach (int n in numbers)
{
if (n > 0)
{
yield return n;
}
else
{
yield break;
}
}
}
var col = TakeWhilePositive(new int[] {1, 2, 4, -1, 4});
Console.WriteLine(col.Count()); //336. Что такое класс?
Это структура данных, которая объединяет состояния (поля) и действия (методы и другие функции-члены) в единое целое.
Класс предоставляет определение для динамически создаваемых экземпляров класса, также известных как объекты.
Классы объявляются с помощью ключевого слова class, за которым следует имя и тело класса.
37. Чем отличается класс от абстрактного класса?
Абстрактный класс — это класс, у которого не реализован один или больше методов.
Класс объявляется абстрактным путем помещения ключевого слова abstract перед определением класса.
Создавать экземпляры абстрактного класса нельзя.
Назначение абстрактного класса заключается в предоставлении общего определения для базового класса, которое могут совместно использовать несколько производных классов.
Абстрактные классы могут определять (но не обязательно) абстрактные методы, не имеющие реализации.
38. Чем отличается абстрактный класс от интерфейса? Для чего нужны интерфейсы и какие задачи они выполняют?
Интерфейс предоставляет только контракт, а абстрактный класс может предоставлять и контракт, и реализацию.
Интерфейсы использовали бы для обеспечения чистой абстракции, а абстрактные классы, когда есть общая реализация для нескольких классов.
Интерфейсы решают проблему множественного наследования.
39. Какие вы знаете модификаторы доступа?
Все типы и члены типов (все объявления, включая вложенные типы) имеют уровень доступности:
- public: код в любой сборке может получить доступ к этому типу или члену.
- protected: только код в том же классе или в производном классе может получить доступ к этому типу или члену.
- internal (по умолчанию для типов): доступ к этому типу или члену может получить только код в одной сборке.
- private (по умолчанию для членов типов): только код, объявленный в том же типе может получить доступ к этому вложенному типу или члену.
- protected internal (менее строгое, чем protected): доступ к этому типу или члену может получить только код в той же сборке или в производном классе в другой сборке.
- private protected (более строгое, чем protected): доступ к этому типу или члену может получить только код в том же классе или в производном классе в той же сборке.
- file: доступ к типу или члену может получить только код в одном файле.
Уровень доступности члена не может быть выше уровня доступности типа, в который он входит.
Производные классы не могут быть более доступны, чем соответствующие базовые типы.
Типы верхнего уровня (на уровне пространства имен) могут быть только public и internal.
40. В чем разница между обычным классом и статическим?
Обычный класс создает экземпляры объектов, а статический класс содержит только статические члены и не может быть инстанциирован.
41. В чем разница переопределения метода между ключевыми словами new и override?
new используется для создания новой реализации метода в производном классе, не связанной с версией в базовом классе.
override используется для замещения виртуального метода из базового класса.
42. Какое различие между const и readonly?
Const - это временная константа, определенная во время компиляции. Должна быть инициализирована при объявлении. Константы могут быть числами, логическими значениями, строками или нулевыми ссылками.
Readonly - это константа времени выполнения, значение которой может быть установлено при объявлении или в конструкторе.
43. Разница между структурой и классом. Приведите примеры структур.
Основные различия: структуры передаются по значению, а классы - по ссылке.
Структуры не поддерживают наследование.
Примеры структур: DateTime, Point, Color.
44. Может ли экземпляр структуры храниться в куче (heap)? Как это сделать?
Если мы создадим экземпляр класса (например, массив), членом которого является структура, то она, как и все остальные данные этого класса окажется в куче.
При упаковке в объект, создается копия экземпляра структуры в куче.
45. Что такое асинхронность и чем она отличается от многопоточности?
В C# следует различать концепции синхронного/асинхронного и однопоточного/многопоточного программирования:
- Синхронная однопоточная. Потоку назначается одна задача и начинается выполнение. Когда завершено выполнение задачи тогда появляется возможность заняться другой задачей.
- Синхронная многопоточная. Каждый поток начинает работать с задачей. Освободившийся поток получает новое задание.
- Асинхронная однопоточная. Поток начав выполнение задачи может приостановить выполнение (при ожидании ответа от внешней системы), сохранив текущее состояние, и начать выполнение другой задачи.
- Асинхронная многопоточная. Одна и та же задача обрабатывается несколькими потоками. При этом достигается максимальное использование потоков.
46. Какие есть ключевые слова для использования асинхронности в коде?
Для использования асинхронности в коде на C# используются следующие ключевые слова: async, await.
47. Что означают ключевые слова async / await?
async: Указывает, что метод может содержать одно или несколько выражений await.
await: Уведомляет компилятор о том, что потребуется возвращаемое методом значение, но не сразу. В результате не нужно блокировать вызывающий поток, и можно продолжать выполнение других задач, пока не потребуется ожидаемое значение.
48. Разница между реляционными и нереляционными базами, плюсы и минусы использования обоих вариантов.
Реляционная база данных — база, где данные хранятся в формате таблиц, они строго структурированы и связаны друг с другом.
Нереляционная база данных — хранит данные без четких связей друг с другом и четкой структуры.
Реляционные базы данных преуспевают в структурированных средах данных, где целостность данных и сложные запросы имеют первостепенное значение.
С другой стороны, нереляционные базы данных блистают в сценариях, требующих масштабируемости, гибкости в моделях данных и высокой производительности в распределенных системах.
49. Что такое индексы в RDBMS?
Индекс - это ключ, созданный на основе одного или нескольких столбцов базы данных, который ускоряет выборку строк из таблицы или представления.
Может быть кластеризованным (таблица сортируется в соответствии со структурой индекса) и некластеризованным (хранится отдельно от таблицы).
Роль индексов состоит в том, чтобы облегчить поиск подмножества строк и столбцов таблицы без необходимости сканировать каждую строку в таблице.
50. Какие типы JOIN существуют в SQL?
- INNER JOIN. Возвращает те строки, для которых в обеих таблицах выполняется условие соединения.
- LEFT JOIN. Возвращает строки, содержащие данные из левой таблицы (указанной слева от ключевого слова JOIN), даже если в правой таблице нет совпадающих строк.
- RIGHT JOIN. Возвращает строки, содержащие данные из правой таблицы (указанной справа от ключевого слова JOIN), даже если в левой таблице нет совпадающих строк.
- FULL JOIN. Возвращает строки, содержащие данные из обеих таблиц. В строках, где нет совпадения слева или справа, все столбцы без совпадения будут заполнены значением NULL.
- CROSS JOIN. Каждая строка одной таблицы соединяется с каждой строкой другой.
51. Для чего нужны unit-тесты?
- Проверка работоспособности отдельных частей исходного кода программы. Это помогает избежать накопления ошибок и снижает риски серьёзных сбоев в работе.
- Проверка нового функционала. Разработчики регулярно обновляют сайты и приложения, добавляют фичи, рефакторят код и вносят правки.
- Упрощение рефакторинга. Unit-тесты позволяют проводить рефакторинг без опасений, что существующая функциональность будет сломана.
- Локализация ошибок. Unit-тесты помогают облегчить обнаружение ошибок и локализовать их поиск.
- Документация. Читая unit-тесты, можно понять, как работают части системы и как их нужно использовать. Это документация, которая живёт и меняется вместе с кодом.
52. Какие преимущества и недостатки использования unit-тестов?
Преимущества использования unit-тестов:
- Простота. Написать тест для отдельного блока кода (модуля) намного проще, чем для всего исходного приложения.
- Информативность. Грамотно сформированный тест помогает разработчикам понять API приложения, функционал модуля и особенности его использования.
- Возможность повторного применения. Созданный ранее тест для проверки одного модуля можно использовать позже для проверки работоспособности компонента ещё раз.
- Поддержка параллельной разработки. Модульное тестирование даёт возможность проверки одного фрагмента кода независимо от других.
Недостатки использования unit-тестов:
- Отсутствие гарантий обнаружения всех имеющихся ошибок. Связано это с тем, что предугадать всё вероятное поведение системы даже в элементарных проектах невозможно.
- Ориентированность на единицу кода (модуля). Такие тесты способны обнаруживать ошибки конкретного фрагмента приложения. Увидеть ошибки, возникающие при интеграции модуля с другими компонентами проекта, не получится.
53. Из каких трех логических блоков состоит unit-тест?
- Arrange (подготовка). В этом блоке готовятся тестовые данные, определяется поведение замокированных зависимостей и выполняются другие подготовительные действия. Таким образом тестируемая система приводится в нужное для теста состояние.
- Act (действие). Вызывается метод тестируемой системы и сохраняется результат, который этот метод возвращает (если таковой есть). Самый маленький блок по объёму, чаще всего одна строка кода — вызов метода.
- Assert (проверка). В этом блоке выполняется серия проверок — например, сравнивается результат, который вернул вызов метода тестируемой системы с ожидаемым. Или проверяется, какие зависимости вызывались, в какой последовательности и какими параметрами. Этот блок отвечает на вопрос, правильно ли работает тестируемая система.
54. Вы набираете google.com в браузере. Расскажите как можно подробнее, что происходит в это время на HTTP-уровне?
- Запрос DNS. Компьютер отправляет запрос на DNS-сервер для разрешения IP-адреса google.com.
- Соединение TCP/IP. Как только IP-адрес определён, компьютер инициирует соединение с сервером по этому IP-адресу.
- Проверка брандмауэром. Брандмауэр сервера проверяет, является ли запрос законным и не несёт ли он вредоносного характера.
- Защита соединения с помощью HTTPS/SSL. Это гарантирует, что данные, которыми обмениваются компьютер и сервер, зашифрованы и не могут быть перехвачены кем-либо другим.
- Передача запроса балансировщику нагрузки. Запрос передаётся балансировщику нагрузки, который отвечает за распределение входящего трафика по нескольким серверам.
- Обслуживание веб-страниц веб-сервером. Веб-сервер извлекает необходимые файлы из хранилища сервера и генерирует HTML-страницу, которая отправляется обратно в браузер.
- Обработка динамического контента сервером приложений. Если HTML-страница содержит динамический контент, который необходимо генерировать «на лету», запрос передаётся серверу приложений, который отвечает за обработку запроса и генерацию контента.
- Запрос к базе данных. Если серверу приложений необходимо извлечь данные из базы данных, он отправляет запрос серверу базы данных.
- Отправка содержимого обратно в браузер. Содержимое отправляется обратно на веб-сервер, который отправляет его обратно в браузер, и пользователь видит домашнюю страницу Google.
55. Как работает HTTPS?
Процесс работы HTTPS делится на несколько этапов:
- Пользователь вводит URL с префиксом «https://» в свой веб-браузер или когда страница перенаправляет пользователя на защищённое соединение. Именно тогда браузер отправляет запрос на установление безопасного соединения с сервером.
- Сервер отвечает на запрос, предоставляя свой публичный сертификат SSL/TLS, который содержит публичный ключ сервера, информацию о сертификате (включая срок действия и издавшую организацию) и цифровую подпись.
- Браузер проверяет сертификат, чтобы убедиться в его подлинности. Он проверяет, что сертификат не истёк и подписан доверенным центром сертификации (CA), и что доменное имя в сертификате соответствует запрашиваемому.
- После проверки сертификата браузер генерирует случайный сеансовый ключ для зашифрованного общения, шифрует его публичным ключом сервера (из сертификата) и отправляет его обратно.
- Сервер использует свой приватный ключ для расшифровки сеансового ключа. Теперь и у клиента, и у сервера есть сеансовый ключ, который используется для шифрования и расшифровки данных, передаваемых между ними.
- Клиент и сервер обмениваются данными, зашифрованными сеансовым ключом. Даже если данные перехватит злоумышленник, он не сможет их прочитать без ключа дешифрования.
- После завершения обмена данными соединение можно временно закрыть с помощью нового сеансового ключа.
56. Как вы понимаете SOLID?
SOLID — это набор основных принципов процесса разработки программного обеспечения, направленных на упрощение чтения, тестирования и сопровождения кода.
- Принцип единственной ответственности (Single Responsibility Principle, SRP). Класс должен меняться только по одной причине. То есть у каждого модуля должно быть только одно назначение, отчего код становится удобнее для восприятия и тестирования.
- Принцип открытости/закрытости (Open/Closed Principle, OCP). Программные сущности (классы, модули и функции) должны быть открыты для расширения, но закрыты для изменения. Это означает, что должна быть возможность изменять внешнее поведение класса, не внося физические изменения в сам класс.
- Принцип подстановки Лисков (Liskov Substitution Principle, LSP). Объекты в программе должны быть заменяемыми экземплярами их базовых типов, не нарушая корректность программы. Это означает, что код, который работает с базовым типом, должен работать и с любым его подтипом, не вызывая ошибок или неожиданного поведения.
- Принцип разделения интерфейса (Interface Segregation Principle). Много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения.
- Принцип инверсии зависимостей (Dependency Inversion Principle). Зависимости внутри системы строятся на основе абстракций. Модули верхнего уровня не зависят от модулей нижнего уровня.
57. Какие протоколы сериализации вы знаете и где они применяются?
- JSON. Популярный протокол, который используется большинством веб-сервисов в качестве формата для обмена данными.
- YAML. Поддерживает разные структуры данных, за счёт чего в этот формат можно сериализовать данные из языков программирования.
- XML. Распространённый формат, основанный на дереве тегов.
- BSON (binary JSON). Похож на JSON, но не является human-readable и оперирует данными в двоичном формате. За счёт этого удобен при хранении и передаче изображений и других вложений.
58. Что такое в вашем понимании чистая функция? Какие у нее преимущества?
Чистая функция — это функция, которая при вызове не влияет на состояние программы и не имеет побочных эффектов. Они возвращают значения только на основе входных аргументов и не изменяют их.
Преимущества чистых функций:
- Проще для тестирования. Результаты их работы можно легко предсказать и проверить.
- Более безопасны. Поскольку они не изменяют состояние программы, то не могут вызвать неожиданные побочные эффекты или ошибки в других частях программы.
- Легче поддаются оптимизации. Поскольку они не имеют побочных эффектов, их можно безопасно кэшировать или выполнять в многопоточной среде.
- Удобнее в сопровождении. Обеспечивают простоту, читаемость и модульную структуру. В результате инженеры-программисты могут легче понимать, изменять и расширять код.
59. Что такое dependency injection и зачем оно нужно?
Dependency injection можно описать как механизм, который позволяет сделать компоненты программы слабосвязанными, а всю программу в целом более гибкой, более адаптируемой и расширяемой.
При использовании Dependency injection не экземпляр класса получает зависимости сам, а они предоставляются ему извне некоторым другим внешним источником. Зачем нужно Dependency injection:
- Разрывает жёсткую связь между классом и его вспомогательными сервисами.
- Улучшает тестируемость кода.
- Позволяет уменьшить число классов, адаптирующих код, при переносе в другие приложения.
- Позволяет проще переносить в другие приложения классы, находящиеся на верхних уровнях (ближе к контроллеру), а нижние (ближе к базе данных) — менять на другие реализации.
60. Что такое cohesion и coupling (связанность и связность)?
Связанность, сопряжение (coupling)— способ и степень взаимозависимости между программными модулями; сила взаимосвязей между модулями; мера того, насколько взаимозависимы разные подпрограммы или модули.
Low Coupling — это принцип, который позволяет распределить обязанности между объектами таким образом, чтобы степень связанности между системами оставалась низкой.
Связность, или прочность (cohesion) — мера силы взаимосвязанности элементов внутри модуля; способ и степень, в которой задачи, выполняемые некоторым программным модулем, связаны друг с другом.
Считается, что объект (подсистема) обладает высокой связностью (High cohesion), если его обязанности хорошо согласованы между собой и он не выполняет огромных объемов работы.
61. Что такое IaaS, PaaS, SaaS и каковы различия между ними?
- IaaS (Infrastructure as a Service) — это облачные серверы, которые можно арендовать, чтобы развернуть собственную ИТ-инфраструктуру. Их используют, если нужно сэкономить время и деньги на организации или масштабировании серверной инфраструктуры.
- PaaS (Platform as a Service) — это платформа с установленным ПО: готовой средой разработки, сервисами для обработки и хранения данных и другими инструментами. Ей часто пользуются разработчики: PaaS сокращает время на организацию базовой инфраструктуры и позволяет быстрее создавать ИТ-продукты.
- SaaS (Software as a Service) — это готовое программное обеспечение. К SaaS-сервисам относятся CRM-системы, трекеры задач, конструкторы сайтов и многое другое. Решение подойдёт, когда нужно быстрое и простое решение для стандартной задачи, например, автоматизации отдела продаж.
62. Какие способы отладки программы вы используете?
- Печать дополнительной информации в консоль, файл или на экран. Это может быть полезно для отслеживания потока выполнения программы.
- Использование брейкпоинтов и точек останова. Они позволяют остановить выполнение программы на определённой строке кода и проверить значения переменных или состояние стека вызовов.
- Использование отладчиков. Отладчики позволяют в режиме реального времени отслеживать выполнение программы. Они позволяют устанавливать брейкпоинты, отслеживать значения переменных и выполнять код по шагам.
- Использование тестовых данных. При создании тестовых данных можно проверять правильность работы программы и обнаруживать ошибки.
63. Какие знаете паттерны? Объясните суть перечисленных.
Паттерны — это шаблоны или образцы, применяющиеся для создания информационных систем. Они предлагают решение типовых задач при разработке программного обеспечения.
Некоторые паттерны и их суть:
- Прототип (Prototype). Шаблон создаётся для представления конкретных объектов. При повторном использовании экземпляра возможна его трансформация или применение базового кода для создания новых элементов команды.
- Фабричный метод (Factory Method). Паттерн, анализирующий внешний вид для объединения нескольких элементов в отдельный супер-класс.
- Абстрактная фабрика (Abstract factory). Шаблон для компоновки объектов по конкретному признаку интерфейса. Ранее определённые классы при этом не используются. Для новой группы создаётся отдельная «фабрика».
- Одиночка (Singleton). Шаблон для формирования эксклюзивной точки софта. Элемент формируется в единственном экземпляре, но наделяется глобальным доступом.
- Строитель (Builder). Образец создания сложных объектов, позволяющий реализовать идею программиста за счёт автоматизированного разделения экземпляра на компоненты — конструирование и представление.
- Декоратор (Decorator). Паттерн проектирования для подключения дополнительных действий и команд. Применяется для расширения и оптимизации функционала сервисов.
- Итератор (Iterator). Открытие доступа к группе или базе данных с внутренней навигацией.
- Цепочка обязанностей (Chain of responsibility). Такой шаблон нужен для организации уровней ответственности в системе.
- Хранитель (Memento). С его помощью можно выполнить закрепление и сохранение внутреннего состояния объекта без нарушения инкапсуляции. Это нужно для того, чтобы в последующем можно было восстановить его в это самое состояние.
64. В чем суть паттерна Singleton? Почему его еще называют антипаттерном?
Суть паттерна Singleton (одиночка) — гарантировать, что у класса будет только один экземпляр. К этому экземпляру будет предоставлена глобальная, то есть доступная из любой части программы, точка доступа. Если попытаться создать новый объект этого класса, то вернётся уже созданный существующий экземпляр.
Синглтон называют антипаттерном по следующим причинам:
- Глобальный доступ. Создаёт проблемы с модификацией, расширением и управлением объектом.
- Нарушение принципа единой ответственности. Объект помимо своей основной функциональности, также отвечает за управление своим глобальным состоянием и доступом из других частей программы.
- Усложнение тестирования. Чрезмерное использование шаблона может привести к жёсткой связи между компонентами, что затруднит тестирование кода и сделает его негибким для будущих изменений.
65. Для чего нужен паттерн Strategy?
Паттерн Strategy — это поведенческий шаблон проектирования, который определяет семейство схожих алгоритмов и помещает каждый из них в собственный класс. После чего алгоритмы можно взаимозаменять прямо во время исполнения программы.
Паттерн Strategy нужен для следующих целей:
- Обеспечение выбора из нескольких вариантов алгоритмов в зависимости от контекста или условий. Каждый вариант реализации алгоритма выделяется в отдельную стратегию.
- Разделение алгоритма на отдельные классы. Это упрощает структуру кода и делает его более модульным.
- Подмена алгоритма во время выполнения программы. Появляется возможность во время выполнения программы динамически выбирать и подменять нужную стратегию и передавать её в основной класс для выполнения операции.
- Изменение поведения объекта. Паттерн позволяет изменять поведение объекта без изменения самого объекта или его наследников.
- Изоляция деталей реализации алгоритмов. Стратегия позволяет изолировать код, данные и зависимости алгоритмов от других объектов, скрыв эти детали внутри классов-стратегий.
66. Какие ключевые различия между распределенными системами и монолитными?
- Монолитная архитектура — это традиционный способ организации приложений, в котором все компоненты продукта находятся внутри одной монолитной кодовой базы.
- Распределённые системы — это совокупность множества отдельных систем, соединённых через сеть общим использованием ресурсов для достижения общих целей.
Монолитная архитектура более уязвима к сбоям, поскольку отказ одного компонента может привести к отказу всего приложения. Распределённые системы более надёжны, так как даже если отдельный блок не работает, остальные остаются работоспособными.
Монолитная архитектура подходит для маленьких и средних приложений с низкой сложностью и нагрузкой. Распределённые системы больше подходят для крупных и сложных проектов, где множество функциональных компонентов и масштабируемость играют ключевую роль.
67. Какие паттерны проектирования распределенных систем вы знаете?
Вот несколько паттернов проектирования распределённых систем:
- API Gateway (API-шлюз). Представляет собой единую точку входа для клиентских запросов к различным внутренним сервисам приложения.
- Pub-Sub (Publish-Subscribe, Издатель-подписчик). В этом паттерне издатели отправляют сообщения в определённые темы, а подписчики получают сообщения из интересующих их тем.
- Request-Response (Запрос-ответ). Базовая модель взаимодействия, где клиент отправляет запрос серверу и ждёт ответа.
- Event Sourcing (Источник событий). Этот паттерн предполагает хранение состояния приложения как последовательности неизменяемых событий.
- ETL (Extract, Transform, Load — Извлечение, преобразование, загрузка). Паттерн для извлечения данных из разных источников, их преобразования и загрузки в целевое хранилище.
- Batch Processing (Сбор и пакетная обработка данных). Этот паттерн предполагает накопление данных или операций перед их обработкой единым пакетом.
- Stream Processing (Потоковая обработка). Этот паттерн обеспечивает непрерывную обработку потоков данных в реальном времени.
- Orchestration (Оркестрация). Паттерн предполагает использование центрального координатора (оркестратора) для управления взаимодействием между распределёнными компонентами или сервисами.
68. Какие есть принципы работы Message bus? Почему могут возникать дубликаты в очередях?
Message Bus можно описать как распределённую систему, которая выступает посредником для обмена сообщениями между различными компонентами системы. Она обеспечивает эффективный, масштабируемый и надёжный способ передачи информации между сервисами, приложениями и системами.
Дубликаты в очередях могут возникать, если:
Приложение завершается сбоем из-за неустранимой ошибки сразу после отправки сообщения. Экземпляр перезапущенного приложения ошибочно считает, что предварительная доставка сообщений не произошла, и последующий запрос отправляет одно и то же сообщение в системе дважды.
Ошибка на уровне клиента или сети возникнет немного раньше, и отправленное сообщение будет зафиксировано в очереди, но подтверждение не будет успешно возвращено клиенту. В этом сценарии клиенту не известен однозначный результат операции отправки.
69. Какие принципы построения идемпотентных сервисов знаете?
Идемпотентность — это способность сервиса получать определённый запрос несколько раз и производить те же побочные эффекты, как если бы запрос был получен только один раз.
Принципы построения идемпотентных сервисов:
- Использование уникальных идентификаторов для запросов. Они позволяют отследить, была ли операция уже выполнена. Если это дубликат, сервер игнорирует запрос, гарантируя отсутствие побочных эффектов.
- Авторизация на основе токенов. Назначаются временные токены для каждого неидемпотентного действия. Как только действие завершено, токен становится недействительным. Если тот же запрос приходит снова с тем же токеном, сервер распознаёт его как недействительный и отклоняет запрос.
- Применение методов, которые при многократном вызове возвращают один и тот же результат. Например, GET, PUT и DELETE. Это позволяет избежать непредвиденных изменений, сохраняя систему в безопасном состоянии.
70. Расскажите, как работают асинхронные методы? Чем асинхронность отличается от параллелизма?
Асинхронные методы подразумевают инициацию некоторой операции, об окончании которой поток, который её инициировал, узнает спустя некоторое время. Обычно это применяется для работы с системой ввода-вывода: диски, сеть и т. д.
Параллелизм — это способ обработки множественных запросов одновременно. В случае асинхронной модели, когда выполняются множество задач, некоторые из них приостанавливаются, а некоторые выполняются.
Асинхронность помогает достичь параллелизма.
71. Какие исключения нельзя остановить в блоке catch?
Некоторые исключения не могут быть обработаны в блоке catch, так как попытка их обработки может привести к непредсказуемому поведению программы: StackOverflowException и ExecutionEngineException.
Также есть исключения, которые не перехватываются и не обрабатываются конструкцией try-catch-finally, например: Corrupted State Exception (исключение повреждённого состояния). Возникает из-за повреждения памяти приложения — в куче или на стеке. В таком случае безопаснее просто мгновенно упасть, не пытаясь что-либо обработать.
72. Какая разница между .NET Standard Class Library и .NET Core Class Library?
.NET Standard — это официальная спецификация для API .NET, которые должны быть доступны во всех реализациях .NET (Framework, Core, Mono, Xamarin, Unity).
Библиотеки, предназначенные для .NET Standard, будут работать в любой среде исполнения, совместимой со стандартом .NET.
Библиотеки, ориентированные на .NET Core, могут работать только в среде выполнения .NET Core.
73. Объясните разницу между отложенным и немедленным исполнением в LINQ. Приведите примеры.
Немедленное выполнение (immediate execution) означает, что запрос выполняется сразу же после его определения, без ожидания дальнейших операций или итераций.
Отложенное выполнение (deferred execution) означает, что запрос выполняется только в тот момент, когда результат требуется.
var numbers = new int[] { 1, 2, 3, 10, 11, 12 };
//немедленное выполнение
var numbersEven = (from num in numbers where (num % 2) == 0 select num).ToList(); //использован метод преобразования ToList()
var count = (from num in numbers where (num % 2) == 0 select num).Count(); //использована агрегатная операция Count()
//отложенное выполнение
var query = from num in numbers where (num % 2) == 0 select num; //создали запрос, он еще не выполняется
foreach (var num in query) //выполнили
Console.WriteLine(num);74. Для чего нужен метод ConfigureServices в Startup.cs?
Метод ConfigureServices в классе Startup.cs используется для настройки сервисов для приложения.
Он регистрирует классы, которые используются приложением, в специальном объекте IServiceCollection. С помощью методов расширений этого объекта производится конфигурация приложения для использования сервисов.
После регистрации класса он становится доступным в любом месте приложения. Достаточно включить его в параметр конструктора класса, где нужно его использовать. Контейнер автоматически внедрит класс.
Метод ConfigureServices вызывается перед методом Configure. Это нужно, чтобы можно было зарегистрировать пользовательскую службу в контейнере, которую можно использовать в методе Configure.
75. Какая разница между services.AddTransient и services.AddScope в ASP.NET Core?
Жизненный цикл сервиса зависит от того, когда создается инстанс зависимости и как долго он существует, а также от того, как мы зарегистрировали этот сервис.
- AddTransient подразумевает, что сервис создается каждый раз, когда его запрашивают. Этот жизненный цикл лучше всего подходит для легковесных, не фиксирующих состояние, сервисов.
- AddScoped - сервис создаются единожды для каждого запроса.
- AddSingleton - сервис создается единожды, а затем каждый последующий запрос будет использовать этот же инстанс.
76. Что такое Kestrel?
Kestrel — это кроссплатформенный веб-сервер для ASP.NET Core. 2 Он основан на кроссплатформенной библиотеке асинхронного ввода/вывода libuv.
77. Опишите ASP.NET MVC request pipeline.
ASP.NET MVC request pipeline — это процесс, через который проходит запрос от клиента перед возвратом ответа. Этапы pipeline:
- Получение запроса. Он проходит через модуль маршрутизации, который сопоставляет входящий запрос с зарегистрированными шаблонами URL в таблице маршрутов.
- Передача запроса обработчику MVC. Обработчик создаёт экземпляр контроллера, который обрабатывает текущий запрос.
- Вызов метода действия контроллером. Контроллер вызывает метод действия с помощью ActionInvoker.
- Отправка ответа клиенту. Происходит выполнение результата, и ответ отправляется в браузер клиента.
- Запрос pipeline контролирует правильную обработку входящих HTTP-запросов и модификацию запроса для генерации соответствующего ответа, подходящего для нужд приложения.
78. Как в ASP.NET WebAPI настроить кэширование ответов на HTTP-запросы?
На уровне middleware:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddResponseCaching();
var app = builder.Build()
app.UseResponseCaching();На уровне контроллера:
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase { ... }На уровне метода контроллера:
[HttpGet("{id}")]
[ResponseCache(Duration =60,Location =ResponseCacheLocation.Client)]
public ActionResult<string> Get(int id) { ... }79. Что такое куча и стек? Различия, принцип работы.
- Стек — это быстрое хранилище памяти, работающее по принципу LIFO и управляемое процессором. Он используется для управления временными данными, которые должны быть обработаны в определённом порядке.
- Куча — это область памяти, в которой выделяются блоки памяти во время выполнения программы. В отличие от стека, выделение и освобождение памяти в куче происходит в произвольном порядке. Куча используется, когда требуется выделить память для переменных или объектов, которые будут использоваться на протяжении всей программы или в течение длительного времени.
Параметры и переменные метода, которые представляют типы значений, размещают своё значение в стеке. Ссылочные типы хранятся в куче.
80. Как работает сборщик мусора?
Сборщик мусора в C# работает следующим образом:
- Он изучает, как объекты размещены в памяти, определяя те из них, до которых может добраться запущенная программа, следуя некоторой цепочке ссылок.
- Когда начинается сборка мусора, сборщик просматривает набор ссылок, называемых корнями. Это участки памяти, которые в силу определённых причин должны быть доступны всегда, и которые содержат ссылки на объекты, созданные программой.
- Сборщик помечает эти объекты как живые, а затем просматривает все объекты, на которые они ссылаются, помечая живыми и их.
- Сборщик мусора продолжает в том же духе, пока не пометит живыми все объекты, которые он смог найти таким способом.
- Когда найдены все живые объекты, остальные могут быть уничтожены, а освободившееся место можно использовать для новых объектов.
- Кроме того, .NET уплотняет память, чтобы в ней не оставалось пробелов, перемещая живые и фактически перезатирая уничтоженные объекты. Это означает, что свободная память всегда находится в конце кучи, что делает выделение новых объектов очень быстрым.
Сборщик мусора автоматически обеспечивает освобождение памяти, занятой объектами, которые больше не используются. Такой подход предотвращает утечки памяти и снимает с разработчика бремя ручного освобождения памяти.
81. Зачем нам зарезервированное слово using в C#, если в .NET есть автоматическое управление памятью? Как с этим связан disposable-паттерн и зачем такой сложный паттерн для managed и unmanaged ресурсов?
Зарезервированное слово using в C# используется для автоматического управления ресурсами. Оно гарантирует, что объекты, реализующие интерфейс IDisposable, будут надлежащим образом удалены, когда в них больше не будет необходимости.
Disposable-паттерн связан с этим следующим образом:
- Объекты предоставляют метод Dispose. Его можно вызывать, когда объект больше не нужен.
- Оператор using определяет границу объекта, за пределами которой объект автоматически уничтожается.
Сложный паттерн для managed и unmanaged ресурсов нужен для:- Явной очистки любых неуправляемых ресурсов. Операции очистки для них должны выполняться в деструкторе C#.
Использование ключевого слова using помогает предотвратить утечки памяти, упростить код за счёт автоматического удаления объектов и способствует созданию более надёжного и эффективного кода.
82. Какие особенности работы с Large Object Heap?
Особенности работы с Large Object Heap (LOH) в C#:
- В LOH помещаются объекты, размер которых больше 85 000 байт. Для других объектов в куче используется Small Object Heap.
- При сборке мусора сжатие памяти в LOH не проводится. Это связано с большими издержками, связанными с размером объектов. Однако благодаря подобному подходу происходит оптимизация приложения.
- Сборщик мусора обрабатывает LOH иначе, чем другие поколения. Вместо сжатия больших объектов он использует алгоритм mark-and-sweep для сбора мусора. Этот алгоритм помечает живые объекты и затем проходит по куче, освобождая память от объектов, на которые больше нет ссылок.
- Новые объекты в LOH располагаются не только один за другим, но и на местах уже свободной памяти, не дожидаясь полной сборки мусора.
- Система возвращает ОС память во время полной сборки мусора, освобождаясь от уже мёртвых объектов из LOH.
83. Когда генерируется дженерик-класс конкретного типа - при выполнении программы или во время компиляции?
Обобщенные классы компилируются JIT-компилятором в родной машинный код, для каждого конкретного типа значения создается новый класс.
При этом все ссылочные типы разделяют общую реализацию (т.к. являются указателями одного размера).
Таким образом, конкретный тип генерируется во время работы программы.
84. Что такое рефлексия?
Рефлексия в C# — это механизм, который обеспечивает доступ к информации о типах во время выполнения приложения.
Рефлексия позволяет:
- просматривать информацию о типах, включая имена, базовые типы и интерфейсы;
- извлекать подписи методов, типы возврата и информацию о параметрах;
- получать доступ и анализировать атрибуты, прикреплённые к типам и членам;
- изучать детали сборки, такие как номера версий и авторы;
- динамически создавать объекты, вызовать методы и получать доступ к свойствам.
85. Расскажите о коллекции LinkedList . Чем она отличается от других коллекций?
В LinkedList элементы хранятся в двусвязном списке. В отличие от других коллекций, где элементы хранятся в динамическом массиве.
LinkedList не обеспечивает прямой доступ к элементам по индексу. Для доступа к элементам требуется проход по списку с начала или конца.
Вставка и удаление элементов в середине связанного списка выполняются быстро, так как не требуется перемещать все элементы.
LinkedList использует дополнительную память для хранения двунаправленных связей между последовательными элементами. Таким образом, объём памяти LinkedList обычно будет больше, чем для других коллекций.
86. Что такое индексатор?
Индексатор в C# — это языковое средство, которое позволяет индексировать экземпляры класса или структуры точно так же, как и массивы.
Индексатор действует как свойство, за исключением того, что его акцессоры (средства доступа к данным) принимают параметры.
Общая форма индексатора: тип_элемента this[int индекс], где тип_элемента обозначает конкретный тип элемента индексатора.
В теле индексатора определены два аксессора (get и set). Они вызываются автоматически при использовании индексатора.
Основное назначение индексатора — предоставить пользователю функциональные возможности, аналогичные работе с массивом. Это полезно при создании специальных типов коллекций, когда их размер заранее неизвестен.
87. Что такое immutable object? Какие преимущества дает использование immutable object? Предложите способ реализации его в .NET.
Immutable object в C# — это объект, состояние которого нельзя изменить после его создания.
Преимущества: потокобезопасность, более простое тестирование и отладка.
Реализация в .NET может включать в себя создание класса с членами только для чтения и без методов для изменения состояния.
88. Когда использовать StringBuilder, а когда string? Как работает StringBuilder?
StringBuilder имеет смысл использовать при большом размере строк или в циклах, где происходит множественное объединение строк, потому что это более эффективный подход.
При работе с небольшим количеством строк использование StringBuilder может быть избыточно. В таких случаях уместно использовать оператор + для конкатенации, поскольку это делает код более читаемым и понятным.
В StringBuilder память для новой строки выделяется только в случае, когда размер новой строки больше размера строки-оригинала. При этом памяти выделяется в два раза больше предыдущего размера. Если размер новой строки меньше или равен размеру строки-оригинала, то память не перераспределяется.
89. Что такое балансирование деревьев?
Балансирование деревьев в контексте структур данных, таких как AVL и красно-черные деревья, обеспечивает равномерное распределение элементов и оптимизацию операций вставки, поиска и удаления.
90. Что такое Key-value структуры?
Key-value структуры, такие как Dictionary<K, V>, предоставляют ассоциативное соответствие между ключами и значениями.
100. Что такое хэш-функция и зачем нужны хэш-таблицы?
Хэш-функция преобразует данные произвольного размера в фиксированный хэш-код. Хэш-таблицы используют хэш-функции для быстрого поиска данных.
101. Какими свойствами должна обладать идеальная хэш-функция?
Идеальная хэш-функция должна обеспечивать равномерное распределение хэш-кодов для различных входных данных.
102. Что такое коллизии и как с ними бороться?
Коллизии возникают, когда два различных ключа получают одинаковый хэш-код.
С ними можно бороться, используя методы разрешения коллизий, такие как цепочки, открытое адресное хэширование и другие.
103. В чем заключается сложность CRUD-операций в Dictionary в .NET?
Операции CRUD в Dictionary<K, V> в .NET имеют асимптотическую сложность O(1) в среднем случае, благодаря использованию хэш-таблицы.
104. Где хранятся массивы? Массивы примитивных типов?
Массивы в C# хранятся в управляемой куче (managed heap).
Массивы примитивных типов по умолчанию хранятся в куче, но могут храниться в стеке при использовании stackalloc.
105. В чем отличие между массивом (T [ ]) и списком (List )?
Массив (T[]) имеет фиксированный размер, в то время как список (List) динамически расширяется.
106. В чем разница между IList и IEnumerable ?
IEnumerable предоставляет только возможность перечисления элементов, в то время как IList расширяет его, добавляя методы для доступа, добавления, удаления и модификации элементов коллекции.
107. Зачем нужны Enumerable, Observable, AsyncEnumerable и какие модели получения данных они реализуют?
- Enumerable предоставляет методы для запросов к коллекциям.
- Observable используется в реактивном программировании для работы с потоками данных.
- AsyncEnumerable предоставляет асинхронные версии методов для работы с коллекциями.
108. В чем разница между IEnumerable и IQueryable?
- IEnumerable представляет коллекцию, выполняющую запросы на стороне клиента (после загрузки данных).
- IQueryable позволяет строить запросы, выполняющиеся на стороне сервера.
Реализация IQueryable используется, например, в Entity Framework для выполнения запросов к базе данных.
109. Что такое enum flags?
Это атрибут, который позволяет типу перечисления представлять комбинацию значений с помощью побитовых операций.
При этом необходимо определить константы перечисления в степенях двойки.
Это полезно, когда перечислению нужно хранить несколько значений одновременно.
[Flags]
enum Days
{
Not = 0,
Mon = 1 << 0,
Tue = 1 << 1,
Wed = 1 << 2,
Thu = 1 << 3,
Fri = 1 << 4,
Sat = 1 << 5,
Sun = 1 << 6,
WorkingDays = Mon | Tue | Wed | Thu | Fri,
Weekend = Sat | Sun
}
var monday = Days.Mon;
if ((monday & Days.WorkingDays) != 0)
{
Console.WriteLine("Понедельник - рабочий день");
}
var badDays = Days.Fri | Days.Mon;
Console.WriteLine($"Плохие дни: {badDays}");110. Расскажите о нормальных формах в СУБД.
Нормальные формы в системах управления базами данных (СУБД) представляют собой ряд рекомендаций, которые помогают гарантировать эффективное, организованное и свободное от аномалий данных проектирование базы данных.
- Первая нормальная форма (1NF). Это самый базовый уровень нормализации. В 1NF каждая ячейка таблицы должна содержать только одно значение, а каждый столбец должен иметь уникальное имя.
- Вторая нормальная форма (2NF). Устраняет избыточные данные, требуя, чтобы каждый неключевой атрибут зависел от первичного ключа. Это означает, что каждый столбец должен быть напрямую связан с первичным ключом, а не с другими столбцами.
- Третья нормальная форма (3NF). Основывается на 2NF, требуя, чтобы все неключевые атрибуты были независимы друг от друга. Это означает, что каждый столбец должен быть напрямую связан с первичным ключом, а не с какими-либо другими столбцами в той же таблице.
- Нормальная форма Бойса-Кодда (BCNF). Это более строгая форма 3NF, которая гарантирует, что каждый определитель в таблице является ключом-кандидатом.
- Четвёртая нормальная форма (4NF). Применяется для устранения многозначных зависимостей — таких зависимостей, где столбец с первичным ключом имеет связь один-ко-многим со столбцом, который не является ключом.
- Пятая нормальная форма (5NF). Разделяет таблицы на более малые таблицы для устранения избыточности данных. Разбиение идёт до тех пор, пока нельзя будет воссоздать оригинальную таблицу путём объединения малых таблиц.
- Шестая нормальная форма (6NF). Каждое ограничение в связях между таблицами должно зависеть только от ограничений ключа и ограничений домена, где домен представляет набор допустимых значений для столбца. Эта форма предотвращает добавление недопустимых данных путём установки ограничения на уровне отношений между таблицами, но не на уровне таблиц или столбцов.
Каждая последующая нормальная форма расширяет ту, которая была до неё. Конкретные требования и свойства хранимых данных определяют, какую нормальную форму следует использовать.
111. Что такое индекс в БД?
Индекс в базе данных (БД) — это объект, создаваемый с целью повышения производительности поиска данных.
Он формируется из значений одного или нескольких столбцов таблицы и указателей на соответствующие строки таблицы и позволяет искать строки, удовлетворяющие критерию поиска.
Ускорение работы с использованием индексов достигается за счёт того, что индекс имеет структуру, оптимизированную под поиск — например, сбалансированного дерева.
112. Когда следует использовать индексы? Преимущества и недостатки.
Индексы следует использовать:
- При поисковых условиях (WHERE). Столбцы, часто участвующие в условиях поиска, являются хорошими кандидатами для индексации.
- Для ускорения сортировки (ORDER BY). Индексы нужно создавать на столбцах, по которым часто производится сортировка.
- Для улучшения производительности операций группировки (GROUP BY).
Преимущества индексов:
- Ускорение запросов. Индексы значительно уменьшают время выполнения запросов.
- Улучшение производительности. Операции поиска, сортировки и группировки становятся более эффективными.
- Поддержка уникальности и целостности данных. Уникальные индексы помогают гарантировать уникальность значений.
Недостатки индексов:
- Затраты на обновление. При вставке, обновлении или удалении данных индексы должны быть обновлены, что может привести к накладным расходам.
- Занимаемое место. Индексы занимают дополнительное место на диске.
- Не всегда подходят. Некоторые операции, такие как добавление или обновление данных в больших объёмах, могут замедлиться из-за индексов.
113. Какие типы индексов существуют? Чем они отличаются?
Кластеризованный индекс сортирует строки с данными в таблице. В таблице может присутствовать только один кластеризованный индекс.
Некластеризованные индексы имеют структуру, отдельную от строк данных. В некластеризованном индексе содержатся значения ключа некластеризованного индекса, и каждая запись значения ключа содержит указатель на строку данных, содержащую значение ключа. Существует возможность создания более одного некластеризованного индекса.
114. Что такое ACID?
ACID — набор требований к транзакционной системе, обеспечивающий наиболее надёжную и предсказуемую её работу.
- Атомарность (atomicity). Гарантирует, что никакая транзакция не будет зафиксирована в системе частично. Будут либо выполнены все её подоперации, либо не выполнено ни одной.
- Согласованность (consistency). Транзакция, достигающая своего нормального завершения и тем самым фиксирующая свои результаты, сохраняет согласованность базы данных. Другими словами, каждая успешная транзакция по определению фиксирует только допустимые результаты.
- Изолированность (isolation). Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат.
- Устойчивость (durability). Независимо от проблем на нижних уровнях (к примеру, обесточивание системы или сбои в оборудовании) изменения, сделанные успешно завершённой транзакцией, должны остаться сохранёнными после возвращения системы в работу.
115. Какие вы знаете уровни изоляции транзакций?
- Read uncommitted (чтение незафиксированных данных). Низший уровень изоляции. Если несколько параллельных транзакций пытаются изменять одну и ту же строку таблицы, то в окончательном варианте строка будет иметь значение, определённое всем набором успешно выполненных транзакций.
- Read committed (чтение фиксированных данных). Большинство промышленных СУБД, в частности, Microsoft SQL Server, PostgreSQL и Oracle Database, по умолчанию используют именно этот уровень. На этом уровне обеспечивается защита от чернового, «грязного» чтения.
- Repeatable read (повторяющееся чтение). Грязные чтения, неповторяющиеся чтения и потерянное обновление предотвращены, но могут возникать фантомные чтения.
- Serializable (упорядочиваемость). Самый высокий уровень изолированности; транзакции полностью изолируются друг от друга. Результат выполнения нескольких параллельных транзакций должен быть таким, как если бы они выполнялись последовательно.
- Snapshot (моментальный срез). Этот вид изоляции не входит в рекомендации стандарта SQL 92, но он реализован во многих СУБД. Процессы-читатели не ждут завершения транзакций писателей, а считывают данные, точнее их версию, по состоянию на момент начала своей транзакции.
116. Что такое план выполнения запроса (execution plan) в MS SQL?
План выполнения запроса (execution plan) в Microsoft SQL Server — это набор конкретных действий, выполнение которых приведёт SQL-запрос к итоговому результату.
Он определяет, как именно будет выполняться пользовательский запрос: в каком порядке будет осуществляться доступ к исходным данным, какие методы будут использоваться для извлечения данных из каждой таблицы, для вычислений, фильтрации, статистической обработки и сортировки данных.
117. Проблема: запрос долго выполняется. Какие есть методы ее диагностики и решения?
Оптимизировать запрос или структуру таблицы. Для этого нужно проанализировать план выполнения с помощью инструмента EXPLAIN и внести корректировки. Например, добавить индекс на те поля, которые используются в узле, для которого планировщик использует Seq Scan и отфильтровывает большое количество строк.
118. Как ORM (Entity Framework или Entity Framework Core) транслируют C# код в язык запросов базы данных? Что для этого используется?
Entity Framework Core транслирует C#-код в язык запросов базы данных, используя технологию LINQ to Entities. В её основе лежит язык интегрированных запросов LINQ (Language Integrated Query).
Процесс трансляции:
- Запрос LINQ обрабатывается Entity Framework Core для создания представления, готового к обработке поставщиком базы данных. Результат кэшируется, чтобы не выполнять эту обработку каждый раз при выполнении запроса.
- Результат передаётся поставщику базы данных.
- Поставщик базы данных определяет, какие части запроса могут выполняться в базе данных. Эти части запроса преобразуются в язык запроса базы данных (например, SQL для реляционной базы данных).
- Запрос отправляется в базу данных, и возвращается результирующий набор (результатами являются значения из базы данных, а не экземпляры сущностей).
Для трансляции выражений LINQ в выражения, понятные для конкретной СУБД (как правило, в выражения SQL), используется Entity Framework при выполнении запроса.
119. Для чего использовать Task.ConfigureAwait?
Поведение по умолчанию await заключается в захвате текущего контекста при запуске задачи и возобновлении продолжения в том же контексте, когда задача завершена. Это может быть сопряжено с взаимоблокировками и потерей производительности.
Чтобы избежать этих проблем, мы можем использовать ConfigureAwait(false) для того, чтобы await не захватывал и не возобновлял работу в текущем контексте, а вместо этого возобновлял работу в любом доступном потоке.
120. Например, есть веб-сервер, который по HTTP-запросу делает выборку из базы данных. Всего на сервере 16 тредов (threads). Каждый HTTP-request выполняет запрос в базу и ожидает результатов, в этом случае тред блокируется. Можно ли оптимизировать эту работу средствами .NET?
- Использовать асинхронное программирование. Это позволит использовать небольшой пул потоков для обработки множества одновременных запросов без ожидания завершения выполнения синхронной задачи.
- Кэшировать часто используемые данные. В зависимости от сценария можно использовать MemoryCache или DistributedCache.
- Минимизировать количество запросов. Цель — получить данные за один запрос, не делая несколько запросов.
- Не извлекать больше данных, чем необходимо. Запросы должны вернуть только те данные, которые необходимы для текущего HTTP-запроса.
121. Зачем нужен ThreadPool? Опишите механику работы: как поток выделяется и возвращается обратно в ThreadPool.
ThreadPool — это пул уже созданных рабочих потоков, которые доступны приложению по мере необходимости. Когда поток в пуле завершает свою задачу, он возвращается в очередь потоков в состоянии ожидания, и с этого момента его можно использовать вновь.
Пул потоков управляет потоками эффективно, уменьшая количество создаваемых, запускаемых и останавливаемых потоков.
Работа ThreadPool выглядит так:
- Пул создаёт несколько потоков заранее.
- Когда приложению подают задачу в пул, он добавляет задачу в очередь и назначает для её выполнения поток из пула.
- Когда поток завершает задачу, пул возвращает его в пул, и он готов к выполнению следующей задачи.
Таким образом, пул может повторно использовать потоки и избегать затрат на создание и уничтожение потоков для каждой задачи, что очень дорого с точки зрения времени и ресурсов.
122. Какие ещё практики, кроме ООП, использовали (AOP, FP и т. д.)?
- Аспектно-ориентированное программирование (AOP) — это парадигма, которая позволяет отделять второстепенные задачи от основной логики методов и классов. Для этого к уже существующему коду добавляется дополнительное поведение без изменений в изначальном коде.
- Функциональное программирование (FP) — это парадигма программирования, в которой программы строятся из функций. Функции — это блоки кода, которые принимают входные данные и возвращают выходные данные. В функциональном программировании функции не изменяют состояние программы, а просто вычисляют значения.
- Декларативное программирование (DP) — это парадигма программирования, в которой задаётся спецификация решения задачи, то есть описывается ожидаемый результат, а не способ его получения.
123. Назовите три самые сложные проблемы, которые вам приходилось решать. Как вы это сделали, как пришли к этому решению?
Три сложные проблемы, которые приходилось решать в программировании, и способы их решения:
- Недостаточный объём знаний технологии. Правильный выбор технологического набора инструментов и фреймворков, используемых при разработке программного обеспечения, может гарантировать стабильную базовую производительность продукта.
- Непонятность кода, написанного другим программистом. Чтобы решить проблему, можно посмотреть на код других людей и попытаться понять, как они использовали логику для решения проблем. Также можно обратиться за помощью к онлайн-сообществам, задать вопросы на форумах или обратиться к наставнику.
- Устранение дефектов, которые не видны пользователям системы. Основная цель переработки или перепроектирования (рефакторинга) кода — сделать его более эффективным и удобным в обслуживании. Чтобы решить проблему, нужно делать переработку кода медленно, но неуклонно. Постепенно код станет более компактным и читаемым.
124. Что такое слабосвязанный код? Чем он лучше сильносвязанного кода? Как бы вы достигали более слабой связности кода?
Слабосвязанный код — это код, который написан таким образом, при котором один фрагмент кода может быть заменён на другой фрагмент кода, не требуя при этом изменений в других фрагментах кода.
Преимущество слабосвязанного кода перед сильносвязанным в том, что можно с меньшими затратами сил и времени менять часть программы.
Для достижения более слабой связности кода можно использовать:
- Иерархическую декомпозицию. Код разделяют на несколько больших частей (подсистем), затем эти подсистемы дробятся на более мелкие сущности. В процессе дробления происходит перегруппировка связей, иногда можно ввести посредника, на которого замкнуть все эти связи.
- Использование интерфейсов. 48 Классы могут взаимодействовать через интерфейсы, а не через другие конкретные классы.
- Внедрение зависимостей (DI). Передача экземпляра происходит через конструктор, что позволяет в реальном времени менять нужные зависимости, не влезая в код класса.
125. Использование статических классов повышает или понижает связность кода?
Использование статических классов в C# увеличивает связность кода, так как интерфейсы не содержат статических методов, а значит, вызвать статический класс или метод можно только явно.
126. Как можно измерить performance кода? Влияет ли факт замеров на производительность?
Измерение производительности кода — это процесс оценки скорости его выполнения. Например, с помощью бенчмаркинга, когда оцениваемый код выполняется множество раз и в конце выдаётся отчёт с результатами.
Сам факт замеров не влияет на производительность кода.
127. Для чего используются и как работают multi-stage билды в Docker?
Multi-stage билды в Docker используются для создания компактных и эффективных контейнерных образов. Они позволяют разработчикам строить несколько промежуточных образов внутри одного Dockerfile, и каждый промежуточный образ служит определённой цели в процессе сборки.
128. Как понять, что какая-то часть кода утилизирует много памяти или долго выполняется? Что может быть ботлнеком в разных случаях? Какие есть способы уменьшения памяти и трафика памяти?
Чтобы понять, что какая-то часть кода утилизирует много памяти или долго выполняется, можно воспользоваться инструментами профилирования. Они позволяют отслеживать распределение памяти и сборку мусора в режиме реального времени, предоставляя информацию о том, какие объекты создаются и как используется память.
Бутылочным горлышком (bottleneck) в контексте кода называют узкие места — элементы, на которых программа «тормозит».
Несколько способов уменьшить потребление памяти в .NET-приложениях:
- Уменьшить количество объектов и следить за тем, чтобы ни один экземпляр не удерживался дольше, чем требуется.
- Использовать типы значений вместо ссылочных типов. Это позволит увеличить объём памяти в стеке, но нужно учитывать, что пространство стека по умолчанию составляет всего 1 МБ.
- Избегать объектов размером более 85 000 байт. Они попадут в LOH, который не уплотняется и может легко фрагментироваться.
- Изменить критические структуры данных с типов классов на типы структур. Это повлияет на семантику использования этих типов: параметры и результаты теперь будут передаваться по значению, а не по ссылке.
- По возможности использовать одну версию .NET для всех проектов. Это позволит сэкономить оперативную память и ускорить «холодный» старт, так как в памяти приложения не будет копий одних и тех же библиотек под разные версии .NET.
129. Как бы вы реализовали cross-cutting concern (например, логирование, валидация, транзакции)?
Cross-cutting concern — это сквозная функциональность, не относящаяся напрямую к выполняемой задаче, но необходимая.
Что можно сделать:
- Отделить эти задачи от основной бизнес-логики. Это соответствует принципам чистой архитектуры и делает основные бизнес-правила более чистыми и адаптируемыми.
- Использовать поведение конвейера. Оно позволяет изолировать логику валидации от бизнес-логики и проверять каждый запрос до достижения основной логики обработки.
- Применить декораторы. Например, для включения глобальной валидации достаточно объявить декоратор и зарегистрировать его для всех обработчиков команд. Тогда для всех реализаций интерфейса валидация будет происходить в декораторе, а код обработчиков останется простым.
130. Расскажите о Rest Maturity Model.
Модель зрелости REST-сервисов — это способ оценить, насколько сервис соответствует принципам RESTful на основе уровней его реализации.
Модель разделяет элементы REST-подхода на 3 основных уровня и один начальный:
- Уровень 0. Сервис имеет единственный URL, использующий единственный HTTP-метод (например, POST).
- Уровень 1. Подобные сервисы используют несколько URL, но единственный HTTP-метод.
- Уровень 2. Сервисы такого уровня имеют много ресурсов и используют несколько HTTP-методов для каждого ресурса (GET, HEAD, POST, PUT, DELETE, TRACE, CONECT).
- Уровень 3. При вызове сервиса он возвращает не только состояние, но и ссылки на то, что можно дальше делать с этим сервисом. Пользователь получает возможность выбирать дальнейшие действия из доступного списка.
131. Что такое CPU и IO-bound задачи?
- CPU-bound задачи в C# — это операции, которые в основном зависят от мощности процессора. К ним относятся сложные вычисления, математические операции или любые операции, требующие значительных ресурсов процессора. Примеры CPU-bound задач: алгоритмы сортировки, математические вычисления и обработка изображений.
- IO-bound задачи — это операции, которые включают в себя операции ввода-вывода, такие как чтение из или запись в файл, запрос к базе данных или выполнение сетевых запросов. IO-bound задачи обычно ограничены скоростью устройств ввода-вывода, а не процессора.
Для оптимизации CPU-bound задач в C# можно использовать параллельную обработку, асинхронное программирование и многопоточность для распределения нагрузки по нескольким ядрам процессора. Для IO-bound задач — асинхронное программирование и неблокирующие операции ввода-вывода.
132. Что такое маршалинг?
Маршалинг — это процесс создания моста между управляемым и неуправляемым кодом. Это подсистема, которая передаёт сообщения из управляемого окружения в неуправляемое и обратно.
Маршалинг объекта — это запись состояния объекта и его кодовой базы таким образом, чтобы была возможность получить копию объекта путём загрузки определений класса исходного объекта.
Используется маршалинг в следующих случаях:
- передача данных между приложениями или процессами;
- передача данных между различными языками программирования;
- использование библиотек, написанных на других языках программирования (например, использование C++ библиотек в C# проекте).
Маршалинг удобен, когда нужно работать с неуправляемым кодом, например, обращаться к Windows API или использовать компоненты COM.
133. Как работает async / await (подробно)? Почему нельзя использовать async void методы?
Асинхронный метод выполняется синхронным образом до тех пор, пока не будет достигнуто первое выражение await.
Если асинхронная операция к этому моменту завершена, то выполнение продолжается синхронно, в том же потоке.
Если асинхронная операция не завершена, то сохраняется код, который надо будет вызвать по завершении асинхронной операции, а поток возвращается в пул потоков и становится доступен для использования. По >завершении операции, берется новый поток, который выполнит продолжение.
Использовать async void методы нельзя, потому что это приводит к следующим проблемам: невозможность перехватить ошибки, невозможность ожидать выполнение метода, сложности с отладкой.
134. Как работает lock? Можно ли использовать структуры внутри выражения lock?
Оператор lock в C# работает для синхронизации потоков и ограничения доступа к разделяемым ресурсам. Он определяет блок кода, внутри которого весь код блокируется и становится недоступным для других потоков до завершения работы текущего потока. Остальные потоки помещаются в очередь ожидания и ждут, пока текущий поток не освободит данный блок кода.
Оператор lock работает с объектами, а при передаче типа значения (структуры) каждый раз при выполнении lock тип значения преобразуется в новый (временный) объект, что не обеспечивает синхронизацию потоков.
135. Что такое Expression Tree?
Expression Tree - это древовидная структура, представляющая выражение в виде дерева, которое можно анализировать и трансформировать во время выполнения.
В LINQ, например, запросы могут быть представлены в виде Expression Trees для последующего выполнения на стороне сервера.
136. Как работает сборщик мусора (подробно)? Почему в GC три поколения, а не, скажем, пять, десять или два?
Сборщик мусора (GC) работает следующим образом:
- Когда среда CLR обнаруживает потребность в дополнительной памяти, запускается сборщик мусора. Он сначала анализирует объекты из поколения 0 — новые объекты, которые ещё ни разу не подвергались сборке мусора.
- Те объекты, которые остаются актуальными после очистки, повышаются до поколения.
- Если после обработки объектов поколения 0 всё ещё необходима дополнительная память, то сборщик мусора приступает к объектам из поколения. Те объекты, на которые уже нет ссылок, уничтожаются, а те, которые по-прежнему актуальны, повышаются до поколения.
- Поскольку объекты из поколения 0 являются более молодыми и нередко находятся в адресном пространстве памяти рядом друг с другом, то их удаление проходит с наименьшими издержками.
В GC три поколения, а не больше или меньше, для того, чтобы снизить издержки от работы сборщика мусора. Идея поколений заключается в предположении, что объекты, созданные недавно, скорее всего, будут быстро освобождены, поэтому сборщик мусора будет чаще пытаться уничтожить именно их.
137. Как бы вы организовали трассировки Web API сервисов?
Трассировка Web API сервисов позволяет проследить путь запроса через систему, получив детальное представление о взаимодействии между различными компонентами. Она помогает выявить узкие места в производительности приложения.
Чтобы включить трассировку, нужно настроить Web API использовать реализацию интерфейса ITraceWriter через объект HttpConfiguration. Для этого можно использовать, например, класс System.Diagnostics.Trace, и пересылать события инструментирования в него.
138. Как в .NET Core можно настроить хранение секретов на компьютерах разработчиков и на рабочих средах?
Для настройки хранения секретов на компьютерах разработчиков в .NET Core можно использовать менеджер секретов. Для работы с ним нужно установить пакет Microsoft.Extensions.Configuration.SecretManager в файле проекта. Затем указать значение секретов в командной строке с помощью команды dotnet user-secrets. Секреты будут храниться в файле JSON в каталоге профилей пользователей (в зависимости от ОС), отдельно от исходного кода.
139. Как бы вы организовали процесс CI/CD .NET Core сервисов для их деплоймента в облачную инфраструктуру?
Процесс CI/CD можно описать так: при внесении изменений в код автоматически происходит сборка, тестирование и развёртывание приложения.
- Использование GitLab. В корневой папке репозитория создают файл конфигурации GitLab CI (.gitlab-ci.yml). В нём определяют этапы и задачи, например сборку и тестирование .NET-проектов. Для сборки используют команду dotnet build, а для тестирования — dotnet test. После определения заданий сборки и тестирования GitLab CI/CD будет автоматически запускать их при внесении изменений в репозиторий.
- Использование Azure Pipelines. Создают конвейер и выбирают шаблон ASP.NET Core. При этом автоматически добавляются задачи, необходимые для сборки кода в примере репозитория. На странице «Конвейеры» создают и тестируют приложение с помощью выбранного шаблона и публикуют артефакт. На странице выпуска используют универсальный шаблон развёртывания службы приложение Azure для развёртывания артефакта.
140. Как включить CORS в AspNetCore?
Чтобы включить CORS в ASP.NET Core, нужно:
- Установить пакет Microsoft.AspNetCore.Cors. Для этого нужно перейти в «Инструменты» — «NuGet Package Manager» — «Управление пакетами NuGet для решения», найти Microsoft.AspNetCore.Cors и установить пакет.
- Добавить сервисы CORS в файл Startup.cs. Для этого нужно вызвать метод services.AddCors().
- Добавить middleware CORS в конвейер запроса. Для этого используется метод UseCors. При этом middleware CORS должно идти перед любыми определёнными конечными точками в приложении, которые нужно поддерживать кросс-доменными запросами (например, перед вызовом UseMvc).
Например, чтобы указать, что приложение может обрабатывать запросы с любых доменов/адресов, нужно вызвать методapp.UseCors(builder => builder.AllowAnyOrigin()).
141. Как реализованы дженерики?
Дженерики в C# реализованы следующим образом:
- Компилятор C# генерирует IL.
- Компилятор JIT генерирует машинный код из IL.
- Когда JIT впервые сталкивается с универсальным методом или методом универсального типа, созданным с определённым набором параметров типа, он генерирует новый машинный код для этого экземпляра.
Для ссылочных типов JIT генерирует одну обобщённую реализацию для всех ссылок, так как под капотом все ссылки относятся к одному и тому же типу данных — адресу в памяти, то есть просто числу. Для значимых типов, у которых разная структура и размер, CLR генерирует по отдельной реализации на каждый значимый тип, который используется как параметр дженерика.
142. Как создать собственный immutable-тип?
Неизменяемость — это свойство структур данных, при котором значение объекта не может измениться после его создания.
Чтобы создать неизменяемый класс, нужно сделать все поля доступными только для чтения и запечатать класс.
Также можно использовать типы записей (records).
public record Order {
public int Id { get; init; }
public string Status { get; init; }
public bool IsPaid { get; init; }
}Свойство, сеттер которого отмечен как init, может быть присвоено только при создании экземпляра объекта. Любая попытка впоследствии изменить значение одного из таких свойств приведёт к ошибке компиляции.
143. Как работает IEnumerable (подробно)?
IEnumerable в C# — это интерфейс, который представляет последовательность объектов, которые можно перечислить.
Он содержит один метод GetEnumerator(), который возвращает объект IEnumerator, который можно использовать для итерации по последовательности объектов. IEnumerator содержит две важные функции:
- Current — возвращает текущий элемент в последовательности;
- MoveNext() — перемещает итератор к следующему элементу в последовательности.
IEnumerable использует отложенное выполнение. Это означает, что вычисления не будут выполняться до тех пор, пока не начнётся перечисление (то есть пока не будет запущен цикл foreach). Во время перечисления возвращённого объекта запрос выполняется, и выданный элемент помещается в выходную последовательность.
144. Какой алгоритм использует коллекция STACK?
Коллекция Stack в C# использует алгоритм LIFO («последний вошёл — первый вышел»).
При такой организации каждый следующий добавленный элемент помещается поверх предыдущего. Извлечение из коллекции происходит в обратном порядке — извлекается тот элемент, который находится выше всех в стеке.
145. Какие структуры данных вы реализовывали сами для платформы .NET? Расскажите, чем они отличались от стандартных реализаций.
Стандартный связанный список LinkedList является двусвязанным. Среди стандартных структур для .NET отсутствует односвязанный список.
Красно-чёрное дерево, B-дерево, биномиальная куча и куча Фибоначчи не реализованы в C#.
146. Чем отличается интерфейс от абстрактного класса? В каких случаях вы использовали бы и то, и другое?
Интерфейс — это абстрактный ссылочный тип, который может содержать некоторое количество методов, свойств, событий и индексаторов. В отличие от абстрактных классов, интерфейс содержит только сигнатуры методов и свойств, но не их реализации. Классы и структуры могут реализовать сразу несколько интерфейсов.
Абстрактный класс — это класс, содержащий один или несколько абстрактных методов. Особенностью такого класса является то, что нельзя создать объект данного класса. Абстрактные классы обычно используются для определения базового класса в иерархии классов.
Одновременно использовать интерфейс и абстрактный класс в C# можно в следующих случаях:
- Для разноплановых классов с общим действием. В этом случае общее действие лучше выносить в интерфейс. А для одноплановых классов с общим состоянием лучше определять абстрактный класс.
- Для определения базового класса в иерархии классов. В этом случае наследование базового класса (абстрактного или обычного) и реализация интерфейсов могут использоваться одновременно. В этом случае в описании класса после имени класса и двоеточия сначала указывается имя базового класса, а затем через запятую перечисляются реализуемые в классе интерфейсы.
147. Почему в структуре нет конструктора по умолчанию?
В структуре C# конструктор по умолчанию (конструктор без параметров) нельзя определять явно, потому что система определяет его для всех структур автоматически и он не подлежит изменению.
Этот конструктор инициализирует поля структуры значениями, задаваемыми по умолчанию.
148. Как БД сохраняет данные?
База данных сохраняет данные в виде упорядоченных и структурированных таблиц, где каждая таблица представляет определённую сущность или взаимосвязь.
Таблицы состоят из строк (записей) и столбцов (атрибутов). Например, при заполнении формы на сайте введённая информация вносится в таблицу базы как одна запись, распределяясь по столбцам и строкам этой таблицы.
149. Какие типы БД вы знаете?
- Реляционные (MySQL, PostgreSQL, Microsoft SQL Server, Oracle) используют структурированный язык запросов (SQL) для управления данными. Они организуют информацию в таблицы с определёнными связями между ними.
- Графовые (Neo4j, Amazon Neptune, InfiniteGraph, InfoGrid) подходят для работы с графами, их узлами и свойствами, а также произвольными отношениями между узлами.
- Документные (CouchDB, MongoDB, Amazon DocumentDB) имеют широкий диапазон применения: от компактной базы данных для одного микросервиса до крупномасштабных решений в качестве хранилища состояния.
- Ключ-значение (Redis и Memcached) используют для кэширования данных или для брокеров сообщений.
150. Как и когда БД лучше использовать?
- Реляционные. Надёжны и гибки, широко применяются в различных приложениях.
- Графовые. Их можно использовать при создании соцсетей или реализации рейтинговой и рекомендательной системы.
- Документные. Они полезны для хранения объектов в одной сущности, но с разными структурами, а также структур (включая объекты, списки и словари), особенно в JSON-подобном формате.
- Ключ-значение. Они подходят для баз, где нужно хранить достаточно простые структуры и иметь к ним быстрый доступ.
В большинстве случаев рекомендуется использовать реляционные базы данных, т.к. имеют свой язык запросов SQL для работы с данными.
151. Что такое денормализации?
Денормализация — намеренное приведение структуры базы данных в состояние, не соответствующее критериям нормализации, обычно проводимое с целью ускорения операций чтения из базы за счёт добавления избыточных данных.
Обычно, денормализация применяется в тех случаях, когда нужно оптимизировать производительность запросов за счёт уменьшения количества объединений или избежать тяжёлые вычисления.
152. Когда и какие уровни изоляции транзакций можно использовать?
Выбор уровня изоляции транзакций зависит от конкретных требований приложения. Более высокий уровень изолированности соответствует лучшей согласованности данных, но его использование может снижать количество физически параллельно выполняемых транзакций. И наоборот, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных.
Стандарт SQL-92 определяет четыре уровня изоляции:
- Read uncommitted (чтение незафиксированных данных). Низший уровень изоляции. Если несколько параллельных транзакций пытаются изменять одну и ту же строку таблицы, то в окончательном варианте строка будет иметь значение, определённое всем набором успешно выполненных транзакций.
- Read committed (чтение фиксированных данных). Большинство промышленных СУБД, в частности, Microsoft SQL Server, PostgreSQL и Oracle, по умолчанию используют именно этот уровень. На этом уровне обеспечивается защита от чернового, «грязного» чтения, но в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы.
- Repeatable read (повторяющееся чтение). Уровень, при котором читающая транзакция «не видит» изменения данных, которые были ею ранее прочитаны. При этом никакая другая транзакция не может изменять данные, читаемые текущей транзакцией, пока та не окончена.
- Serializable (упорядочиваемость). Самый высокий уровень изолированности; транзакции полностью изолируются друг от друга. Результат выполнения нескольких параллельных транзакций должен быть таким, как если бы они выполнялись последовательно.
153. Как в популярных СУБД реализованы принципы ACID (SQL Server, PostgreSQL и т. д.)?
В SQL Server принципы ACID реализованы следующим образом:
- Атомарность. Все операции (вставка, обновление, удаление) внутри транзакции либо происходят, либо нет. Если в середине транзакции возникает ошибка, все уже сделанные изменения автоматически откатываются.
- Согласованность. База данных никогда не останется в полузавершённом состоянии. Если транзакция успешно завершена, все изменения применяются к базе данных. Если в середине транзакции возникает сбой в системе, все уже сделанные изменения автоматически откатываются.
- Изолированность. Каждая транзакция индивидуальна, и одна транзакция не может получить доступ к результату других транзакций до завершения своей. Для этого в SQL Server используются блокировки для блокировки таблицы.
- Долговечность. После завершения транзакции изменения в базе данных будут постоянными. Даже если произойдёт сбой в системе или любые аномальные изменения, это свойство защитит зафиксированные данные.
В PostgreSQL принципы ACID реализованы через комбинацию механизмов:- Атомарность. Достигается с помощью журнала транзакций (WAL). Все изменения, сделанные во время транзакции, сначала записываются в WAL, прежде чем применяться к реальной базе данных. Если транзакция не удаётся или прерывается, PostgreSQL может использовать WAL для отката изменений и восстановления базы данных в прежнее состояние.
- Согласованность. Обеспечивается с помощью различных механизмов, таких как первичные ключи, уникальные ограничения, внешние ключи и контрольные ограничения. Эти механизмы гарантируют, что данные в базе данных соответствуют предопределённым правилам и сохраняют свою целостность.
- Изолированность. Для контроля видимости и поведения блокировок транзакций в PostgreSQL предусмотрены несколько уровней изоляции. По умолчанию используется уровень изоляции Read Committed, который обеспечивает баланс между производительностью и согласованностью.
- Долговечность. Достигается с помощью того же журнала транзакций (WAL). Все изменения, сделанные во время транзакции, сначала записываются в WAL, прежде чем применяться к реальной базе данных. Когда транзакция зафиксирована, PostgreSQL гарантирует, что WAL сбрасывается на диск, делая изменения постоянными.
154. Приходилось ли вам оптимизировать запрос в БД? Если да, то как?
Существуют различные методы оптимизации:
- Анализ и диагностика производительности. Для этого можно использовать команду EXPLAIN, которая показывает, как база данных планирует выполнить запрос. Также можно мониторить производительность с помощью инструментов, таких как New Relic или встроенные средства мониторинга баз данных.
- Оптимизация SELECT-запросов: избегание SELECT*, использование WHERE, ограничение количества возвращаемых строк, использование индексов.
- Оптимизация INSERT, UPDATE и DELETE-запросов: использование пакетных операций.
- Оптимизация структуры базы данных. Нормализация и денормализация данных могут помочь улучшить производительность. Нормализация уменьшает избыточность данных, а денормализация позволяет уменьшить количество JOIN-операций.
- Использование кэширования. Кэширование результатов запросов может значительно уменьшить нагрузку на базу данных и ускорить выполнение запросов.
- Обновление статистики базы данных. Регулярное обновление статистики помогает базе данных лучше планировать выполнение запросов.
155. Опишите, какие вы знаете потенциальные проблемы, связанные с параллельными запросами к БД.
- Утраченные обновления. Возникают, когда несколько транзакций выбирают одну и ту же строку и обновляют её на основе выбранного значения.
- Незафиксированные зависимости. 15 Появляются, когда вторая транзакция выбирает строку, которая обновляется другой транзакцией (грязное чтение).
- Неповторяемое чтение. Возникает, когда вторая транзакция пытается получить доступ к одной и той же строке несколько раз и каждый раз считывает разные данные.
- Неправильная сводка. Происходит, когда одна транзакция суммирует значения всех экземпляров повторяющегося элемента данных, а вторая транзакция обновляет несколько экземпляров этого конкретного элемента данных. В этой ситуации полученная сводка не отражает правильный результат.
156. Какую базу данных вы бы использовали для реализации distributed lock механизма? Расскажите детали реализации.
Для реализации механизма распределённых блокировок можно использовать различные базы данных, например:
- Redis. 15 Хранилище данных в памяти обеспечивает высокую производительность операций. В Redis есть атомарные команды, которые позволяют выполнять сложные операции за один непрерываемый шаг.
- Apache ZooKeeper. Высоконадёжная распределённая служба координации, которая может использоваться для управления блокировками, информацией о конфигурации и распределённой синхронизацией.
- etcd. 13 Распределённая надёжная служба хранения ключей и значений, которая основана на алгоритме консенсуса Raft и предоставляет возможности распределённых блокировок.
- Google Cloud Firestore. Масштабируемая распределённая база данных NoSQL, которая может использоваться для реализации распределённых блокировок с помощью транзакций и условных обновлений.
Детали реализации распределённых блокировок на Redis включают использование команды SETNX для установки блокировки на ключ только в том случае, если он ещё не существует. Также для запроса блокировки на конкретный ресурс можно использовать команду SET resource_name unique_value NX PX duration, где resource_name — значение, которое будет общим для всех инстанций приложения, а unique_value — уникальное для каждой инстанции приложение значение, например UUID. Наконец, указывается длительность (в миллисекундах), после которой Redis автоматически снимет блокировку.
157. Какую проблему решают микросервисы?
Микросервисы решают следующие проблемы:
- Независимость сервисов. Каждая микрослужба работает самостоятельно, у неё своя база данных и кодовая база, что позволяет вносить изменения без влияния на другие сервисы.
- Масштабируемость. Микросервисы можно индивидуально масштабировать в зависимости от спроса, оптимизируя использование ресурсов и обеспечивая высокую производительность.
- Износостойкость. Изоляция сбоев от отдельных сервисов повышает отказоустойчивость и надёжность системы.
- Децентрализованное управление данными. Данные распределяются между микросервисами, что снижает риск повреждения данных и упрощает их управление.
Кроме того, микросервисы позволяют командам работать над отдельными службами независимо, что ускоряет циклы разработки и упрощает обслуживание.
158. Какие есть способы коммуникации микросервисов?
Некоторые способы коммуникации микросервисов:
- Синхронная коммуникация. В этом случае сервисы взаимодействуют в реальном времени, ожидая ответа перед продолжением работы. Распространённые протоколы — HTTP/HTTPS с использованием REST или gRPC.
- Асинхронная коммуникация. Сервисы взаимодействуют без ожидания немедленного ответа, часто через брокеров сообщений, таких как RabbitMQ, Kafka или AWS SQ S.
- Публикация/подписка (Pub/Sub). Это асинхронная форма коммуникации, при которой микросервис публикует сообщение в теме, а все другие микросервисы, подписавшиеся на эту тему, немедленно его получают.
- Потоковая передача событий (Event Streaming). Для бесшовной коммуникации между микросервисами используются потоки событий и сообщения.
- API Gateway. Шлюз находится между микросервисами и клиентским приложением и обеспечивает единую точку входа для клиента.
159. Расскажите варианты реализации распределенных транзакций в микросервисах.
Некоторые варианты реализации распределённых транзакций в микросервисах:
- Шаблон проектирования Saga. Это отдельный микросервис, который через шину ловит сообщения о событиях в других микросервисах. Он сверяется с шаблоном процесса и отдаёт команду другим модулям или другие модули сами подписываются на события и при наступлении события действуют по шаблону процесса.
- Координатор распределённых транзакций. Принцип похож на Saga, но вместо того, чтобы слушать шину данных и отправлять через неё сообщения микросервисам, оркестратор работает напрямую с БД микросервисов.
- Outbox-Inbox. Микросервисы обмениваются сообщениями напрямую, без оркестратора. Например, при переводе денег один микросервис сохраняет у себя в Outbox сообщение об отправке, второй записывает у себя в Inbox сообщение о получении. Если случается сбой, то можно соотнести между собой их ящики и добиться согласованности данных.
- Двухфазный коммит. Управляющий транзакциями сервис информирует сервисы о необходимости подготовиться к транзакции, и если все готовы — командует её выполнить.
160. Что такое circuit breaker?
Circuit Breaker — это механизм обеспечения отказоустойчивости, используемый в архитектуре микросервисов для предотвращения каскадных отказов сервисов в распределённой системе.
Он определяет, когда сервис находится в опасном состоянии, и перенаправляет трафик в сторону от него, поддерживая стабильность системы.
161. Каким образом вы будете налаживать систему, состоящую из множества микросервисов, если нужно отследить полный путь обработки запроса?
Для отслеживания полного пути обработки запроса в системе из множества микросервисов можно использовать распределённую трассировку. Она позволяет мониторить пользовательский запрос и отслеживать путь его прохождения через множество взаимосвязанных служб.
Принцип работы распределённой трассировки:
- Идентификация запроса. После того как сервису приходит запрос, он назначает ему уникальный идентификатор. Он передаётся через заголовки HTTP и другие протоколы.
- Логирование. Каждый сервис логирует идентификатор трассировки и другие метаданные о выполнении запроса и после этого передаёт данные дальше по цепочке сервисов.
- Сбор информации в одном хранилище. В нём происходит агрегация и анализ этих данных.
- Визуализация информации. Строится дерево запросов, по которому можно отследить путь каждого запроса, проанализировать время обработки запроса в каждом конкретном сервисе.
162. Что такое брокеры сообщений? Что такое at-least-once, at-most-once семантика? Есть ли какие-то брокеры, которые гарантируют exactly-once семантику?
Брокер сообщений — это программный компонент, который служит посредником между различными компонентами распределённой системы. Он обрабатывает сообщения, полученные от отправителей, и перенаправляет к соответствующим потребителям.
Семантика доставки сообщений включает:
- At least once. Отправитель получает подтверждение от брокера, что гарантирует однократную запись сообщения в топик. Но если отправитель не получил подтверждения по истечении определённого времени или получил ошибку, он может повторить отправку.
- At most once. Отправитель не повторяет отправку сообщения при отсутствии подтверждения или в случае ошибки. При этом возможна ситуация, что сообщение не записано в топик и не получено потребителем.
- Exactly once. Даже при повторной попытке отправителя отправить сообщение, оно доставляется строго один раз. В случае ошибки, заставляющей отправителя повторить попытку, сообщение будет однократно записано в логе брокера.
Да, некоторые брокеры сообщений гарантируют exactly-once семантику, например, Apache Kafka. Доставка в этом брокере реализована так: каждый пакет сообщений, отправленный в Kafka, содержит порядковый номер, чтобы брокер мог определить наличие данных и исключить их дублирование.
163. Как должен работать код клиента брокера в зависимости от выбранной семантики?
Работа кода клиента брокера зависит от выбранной семантики:
- Для семантики «хотя бы один раз» (at least once) производитель должен дождаться подтверждения от брокера. Если он не получает подтверждения, производитель повторно отправляет сообщение.
- Для семантики «не более одного раза» (at most once) потребитель сначала сохраняет позицию последнего полученного события, а затем обрабатывает его. Если обработка события завершается с ошибкой в середине, при перезапуске потребителя он не может вернуться к чтению старого события. Следовательно, нет гарантии успешной обработки всех полученных событий.
- Для семантики «ровно один раз» (exactly once) сначала обрабатывается полученное событие, а затем результаты где-то сохраняются. В случае сбоя и перезапуска пользователь может перечитать и повторно обработать старые события. Однако любые повторяющиеся события удаляются и не обрабатываются.
164. Какие инструменты для работы с очередями вам известны (как в .NET, так и отдельные продукты), какой инструмент/продукт вы бы выбрали и почему?
Несколько инструментов и продуктов для работы с очередями в .NET:
- DotNetWorkQueue. Библиотека для приложений .NET, которая позволяет работать с рабочими очередями на базе SQL-сервера, SQLite, Redis и PostGreSQL.
- DotNetMQ. Система очереди сообщений с открытым исходным кодом, построенная на C# и .NET framework 3.5. Поддерживает три типа хранилища: SQLite (по умолчанию), MySQL и память.
- Azure Service Bus. Решение брокера сообщений от Azure с такими функциями, как издатели и подписчики, темы, сессии сообщений, автопересылка и другие.
- Azure Queue. Простая реализация очереди на базе Azure Storage.
- Rabbit MQ. Популярный брокер сообщений с открытым исходным кодом, который сочетает удобный интерфейс, хорошую документацию, высокую эффективность и набор функций.
165. Какие виды сервисов бывают в Service Fabric?
В Service Fabric бывают два основных вида сервисов:
- Без состояния. В таком сервисе не поддерживается внутреннее состояние между запросами, для постоянных данных используется внешнее хранилище.
- С состоянием. В таких сервисах внутреннее состояние сохраняется между запросами, данные хранятся непосредственно внутри службы.
166. Какие особенности и ограничения Azure Table Storage?
Особенности Azure Table Storage:
- Гибкое хранилище сущностей ключ-значение. Позволяет быстро создать облачное приложение без замыкания модели данных приложения на конкретный набор схем.
- Разные записи внутри одной таблицы могут иметь разную структуру. Такой подход позволяет эффективно хранить и оперировать простыми реляционными данными.
- Географическая репликация. Данные реплицируются по двум датацентрам, находящимся за несколько сотен миль друг от друга, но на одном континенте, что обеспечивает сохранность в случае катастрофы.
Некоторые ограничения Azure Table Storage:- Имена свойств таблиц должны состоять только из букв и цифр.
- Имя таблицы не должно начинаться с цифры.
- Имена таблиц различают регистры.
- Длина имени таблицы должна быть в пределах от 3 до 63 символов.
- Сущность может иметь не более 255 свойств.
- Свойства «ключ секции» и «ключ строки» не могут быть больше 1Кб размером.
- Суммарный объём всех данных не может превышать 1Мб.
167. Как бороться с проблемой холодного старта в Azure Functions?
Основные способы бороться с проблемой холодного старта в Azure Functions:
- Обновлять зависимости. В проекте должны использоваться самые последние версии основных зависимостей.
- Изменять параметры конфигурации. Недавние оптимизации могут потребовать незначительных изменений в настройках приложения.
- Развёртывать функцию в сжатом пакете .zip. Нужно минимизировать его размер, удалив ненужные файлы и зависимости, например символы отладки (.pdb файлы) и лишние файлы изображений.
- Использовать функцию «всегда готовые экземпляры» в плане Flex Consumption. Этот план поддерживает длительное время выполнения функции и включает частную сеть, выбор размера экземпляра, контроль параллельности и функции быстрого и большого масштабирования.
- Использовать план App Service. В этом случае о холодном старте можно не беспокоиться, так как выделенные вычислительные ресурсы всегда доступны.
168. В чем отличие очередей и топиков в Azure Service Bus?
Отличие очередей и топиков в Azure Service Bus заключается в следующем:
- Очереди обеспечивают одностороннюю связь «один к одному». Каждое сообщение доставляется одному получателю, после обработки оно автоматически удаляется из очереди.
- Топики основаны на модели «издатель — подписчик». Одно сообщение доставляется нескольким подписчикам, которые могут быть отдельными службами или компонентами, заинтересованными в обработке сообщений с определёнными критериями. Каждая подписка имеет уникальный фильтр, который определяет типы получаемых из топика сообщений. Таким образом, очереди используются, когда нужно гарантировать, что каждое сообщение обрабатывается одним получателем только один раз, а топики — когда нужно транслировать сообщения нескольким подписчикам на основе их интересов или критериев.