🤔 Какие есть модификаторы доступа?
Есть модификаторы доступа, которые определяют, кто может использовать классы, методы и переменные. Они помогают скрыть внутренние детали кода и контролировать доступ к данным.
🚩Подробное объяснение с примерами
🟠`public` (Открытый доступ)
Открытый доступ означает, что элемент можно использовать везде.
🟠`private` (Только внутри класса)
Самый закрытый модификатор. Поля и методы невидимы за пределами класса.
🟠`protected` (Доступен в наследниках)
Доступен только внутри класса и его наследников.
🟠`internal` (Только внутри проекта)
Элементы с
🟠`protected internal` (В сборке и у наследников)
Этот модификатор разрешает доступ внутри сборки, а также в классах-наследниках за её пределами.
🟠`private protected` (Только в классе и наследниках из той же сборки)
Этот модификатор ещё жёстче, чем
- Доступ внутри класса – да
- В наследниках – только внутри той же сборки
- В других проектах – нет доступа!
Ставь 👍 и забирай 📚 Базу знаний
Есть модификаторы доступа, которые определяют, кто может использовать классы, методы и переменные. Они помогают скрыть внутренние детали кода и контролировать доступ к данным.
🚩Подробное объяснение с примерами
🟠`public` (Открытый доступ)
Открытый доступ означает, что элемент можно использовать везде.
public class Car
{
public string Model = "Tesla";
}
class Program
{
static void Main()
{
Car car = new Car();
Console.WriteLine(car.Model); // Доступ открыт
}
}
🟠`private` (Только внутри класса)
Самый закрытый модификатор. Поля и методы невидимы за пределами класса.
class Car
{
private string model = "Tesla";
private void PrintModel()
{
Console.WriteLine(model);
}
}
class Program
{
static void Main()
{
Car car = new Car();
// car.model = "BMW"; Ошибка! Поле `model` — private
// car.PrintModel(); Ошибка! Метод `PrintModel` — private
}
}
🟠`protected` (Доступен в наследниках)
Доступен только внутри класса и его наследников.
class Car
{
protected string Model = "Tesla";
}
class ElectricCar : Car
{
public void ShowModel()
{
Console.WriteLine(Model); // Можно, потому что наследуемый класс
}
}
class Program
{
static void Main()
{
ElectricCar eCar = new ElectricCar();
// eCar.Model Ошибка! Поле `Model` доступно только в наследниках
}
}
🟠`internal` (Только внутри проекта)
Элементы с
internal можно использовать только внутри одной сборки (проекта).internal class Engine
{
public void Start() => Console.WriteLine("Двигатель запущен");
}
class Program
{
static void Main()
{
Engine engine = new Engine();
engine.Start(); // Работает, потому что внутри того же проекта
}
}
🟠`protected internal` (В сборке и у наследников)
Этот модификатор разрешает доступ внутри сборки, а также в классах-наследниках за её пределами.
public class Car
{
protected internal string Model = "Tesla";
}
class ElectricCar : Car
{
public void ShowModel()
{
Console.WriteLine(Model); // Можно, потому что наследник
}
}
🟠`private protected` (Только в классе и наследниках из той же сборки)
Этот модификатор ещё жёстче, чем
protected internal: - Доступ внутри класса – да
- В наследниках – только внутри той же сборки
- В других проектах – нет доступа!
class Car
{
private protected string Model = "Tesla";
}
class ElectricCar : Car
{
public void ShowModel()
{
Console.WriteLine(Model); // Можно, потому что наследник в той же сборке
}
}
class Program
{
static void Main()
{
ElectricCar eCar = new ElectricCar();
// eCar.Model Ошибка! `Model` доступен только в наследниках из этой сборки
}
}
Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Почему требуется API между базой данных и клиентским приложением?
API (например, REST или GraphQL) обеспечивает прослойку между клиентом и базой данных, потому что:
1. Безопасность — база данных не должна быть напрямую доступна клиенту.
2. Инкапсуляция логики — в API можно реализовать бизнес-логику и валидации.
3. Гибкость — можно изменять структуру базы, не затрагивая клиента.
4. Масштабируемость — API может обрабатывать нагрузку, кешировать данные и распределять запросы.
5. Контроль доступа — через API проще реализовать авторизацию и аутентификацию.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
API (например, REST или GraphQL) обеспечивает прослойку между клиентом и базой данных, потому что:
1. Безопасность — база данных не должна быть напрямую доступна клиенту.
2. Инкапсуляция логики — в API можно реализовать бизнес-логику и валидации.
3. Гибкость — можно изменять структуру базы, не затрагивая клиента.
4. Масштабируемость — API может обрабатывать нагрузку, кешировать данные и распределять запросы.
5. Контроль доступа — через API проще реализовать авторизацию и аутентификацию.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие методы есть в Action фильтре?
Используются для выполнения логики до и после выполнения метода действия контроллера. Они предоставляют механизм для выполнения кросс-секционных задач, таких как логирование, обработка исключений, валидация и т.д. Action фильтры реализуют интерфейс
🚩Методы в Action фильтре
🟠OnActionExecuting
Этот метод вызывается перед выполнением метода действия. Здесь можно добавить логику, которая будет выполняться до вызова действия, например, логирование или проверка условий.
🟠OnActionExecuted
Этот метод вызывается после выполнения метода действия. Здесь можно добавить логику, которая будет выполняться после вызова действия, например, логирование результатов или модификация ответа.
🟠OnActionExecutionAsync
Этот метод объединяет функциональность
🚩Реализации Action фильтра
Синхронный Action фильтр:
Асинхронный Action фильтр:
🚩Применение Action фильтра
Action фильтр можно применять к контроллерам или действиям контроллера с помощью атрибута
Применение к контроллеру или действию:
Глобальная регистрация:
Ставь 👍 и забирай 📚 Базу знаний
Используются для выполнения логики до и после выполнения метода действия контроллера. Они предоставляют механизм для выполнения кросс-секционных задач, таких как логирование, обработка исключений, валидация и т.д. Action фильтры реализуют интерфейс
IActionFilter или IAsyncActionFilter.🚩Методы в Action фильтре
🟠OnActionExecuting
Этот метод вызывается перед выполнением метода действия. Здесь можно добавить логику, которая будет выполняться до вызова действия, например, логирование или проверка условий.
public void OnActionExecuting(ActionExecutingContext context)
{
// Логика до выполнения действия
}
🟠OnActionExecuted
Этот метод вызывается после выполнения метода действия. Здесь можно добавить логику, которая будет выполняться после вызова действия, например, логирование результатов или модификация ответа.
public void OnActionExecuted(ActionExecutedContext context)
{
// Логика после выполнения действия
}
🟠OnActionExecutionAsync
Этот метод объединяет функциональность
OnActionExecuting и OnActionExecuted в одном асинхронном методе. Здесь можно определить логику, которая будет выполняться как до, так и после выполнения метода действия.public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Логика до выполнения действия
var resultContext = await next();
// Логика после выполнения действия
}
🚩Реализации Action фильтра
Синхронный Action фильтр:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// Логика до выполнения действия
Console.WriteLine("Before executing action");
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Логика после выполнения действия
Console.WriteLine("After executing action");
}
}
Асинхронный Action фильтр:
public class SampleAsyncActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// Логика до выполнения действия
Console.WriteLine("Before executing action");
var resultContext = await next();
// Логика после выполнения действия
Console.WriteLine("After executing action");
}
}
🚩Применение Action фильтра
Action фильтр можно применять к контроллерам или действиям контроллера с помощью атрибута
[ServiceFilter] или [TypeFilter]. Также его можно зарегистрировать глобально в Startup.cs.Применение к контроллеру или действию:
[ServiceFilter(typeof(SampleActionFilter))]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Глобальная регистрация:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(config =>
{
config.Filters.Add(typeof(SampleActionFilter));
});
}
Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Что такое индексы, какие типы бывают, использовал ли их на практике?
Индексы в контексте баз данных — это структуры, которые помогают ускорить операции выборки данных, предоставляя быстрые пути к данным в таблицах. Виды индексов включают кластерные, некластерные, полнотекстовые и другие. Кластерные индексы определяют физический порядок хранения данных в таблице, в то время как некластерные индексы хранят только указатели на данные. Я использовал индексы на практике для улучшения производительности запросов в базах данных.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие есть виды привязок данных и когда применяются?
Привязка данных (Data Binding) — это механизм, который позволяет автоматически синхронизировать данные между источником (например, моделью) и интерфейсом (например, элементами UI).
🚩Односторонняя привязка (One-Way Binding)
Данные идут только в одном направлении: из модели в UI.
- Для вывода данных, которые не должны изменяться пользователем
- Например, для отображения текущего времени
Пример в WPF
🚩Двусторонняя привязка (Two-Way Binding)
Данные синхронизируются в обе стороны: UI ↔️ Модель
- В формах ввода (например,
- Используется в MVVM
Пример в WPF
🚩Привязка к событиям (Event Binding)
Позволяет связывать UI с методами обработки событий.
- Для обработки кнопок (
- В реактивных фреймворках (Blazor, WinForms)
Пример в Blazor
🚩Привязка к командам (Command Binding)
Используется в паттерне MVVM вместо событий
- В WPF и Xamarin
- Позволяет отделить логику от UI
Пример в WPF
🚩Привязка к коллекциям (ItemsSource Binding)
Позволяет привязывать списки к элементам UI (ListBox, DataGrid)
- В списках, таблицах, дропдаунах
Пример в WPF
Ставь 👍 и забирай 📚 Базу знаний
Привязка данных (Data Binding) — это механизм, который позволяет автоматически синхронизировать данные между источником (например, моделью) и интерфейсом (например, элементами UI).
🚩Односторонняя привязка (One-Way Binding)
Данные идут только в одном направлении: из модели в UI.
- Для вывода данных, которые не должны изменяться пользователем
- Например, для отображения текущего времени
Пример в WPF
<TextBlock Text="{Binding UserName}" />🚩Двусторонняя привязка (Two-Way Binding)
Данные синхронизируются в обе стороны: UI ↔️ Модель
- В формах ввода (например,
TextBox), чтобы обновлять данные в модели - Используется в MVVM
Пример в WPF
<TextBox Text="{Binding UserName, Mode=TwoWay}" />🚩Привязка к событиям (Event Binding)
Позволяет связывать UI с методами обработки событий.
- Для обработки кнопок (
Button.Click) - В реактивных фреймворках (Blazor, WinForms)
Пример в Blazor
<button @onclick="IncrementCount">Добавить</button>
@code {
private int count = 0;
private void IncrementCount() => count++;
}
🚩Привязка к командам (Command Binding)
Используется в паттерне MVVM вместо событий
- В WPF и Xamarin
- Позволяет отделить логику от UI
Пример в WPF
<Button Content="Сохранить" Command="{Binding SaveCommand}" />🚩Привязка к коллекциям (ItemsSource Binding)
Позволяет привязывать списки к элементам UI (ListBox, DataGrid)
- В списках, таблицах, дропдаунах
Пример в WPF
<ListBox ItemsSource="{Binding Users}" DisplayMemberPath="Name" />Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое примитив синхронизации semaphore?
Семафор — это ограничитель доступа, который позволяет нескольким потокам одновременно использовать ресурс, но только в пределах установленного количества. Например, если доступно 3 слота — максимум 3 потока могут работать параллельно, остальные ждут.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Семафор — это ограничитель доступа, который позволяет нескольким потокам одновременно использовать ресурс, но только в пределах установленного количества. Например, если доступно 3 слота — максимум 3 потока могут работать параллельно, остальные ждут.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Что такое контекст синхронизации?
"контекст синхронизации" (SynchronizationContext) — это класс, который предоставляет возможность управлять способом, которым операции переключаются обратно в основной поток или контекст для продолжения выполнения после асинхронной операции. Это важно для приложений с графическим пользовательским интерфейсом, таких как Windows Forms и WPF, где доступ к элементам пользовательского интерфейса разрешён только из основного потока.
🚩Как он работает
Абстрагирует модель синхронизации для различных сред выполнения. Например, в приложениях Windows Forms и WPF управление элементами UI должно происходить в главном потоке.
🟠Send
синхронно отправляет делегат на выполнение в контекст синхронизации.
🟠Post
асинхронно отправляет делегат на выполнение в контекст синхронизации.
🚩Применение SynchronizationContext
Используется для того, чтобы после асинхронной операции вернуться в правильный поток и безопасно обновить UI или выполнить код, который требует выполнения в определённом потоке.
🚩Зачем он нужен
🟠Безопасность потоков
Позволяет безопасно обращаться к элементам UI из асинхронных или вторичных потоков.
🟠Правильное управление потоками
Обеспечивает выполнение кода в контексте, для которого он предназначен, что особенно важно в многопоточных и сетевых приложениях.
Ставь 👍 и забирай 📚 Базу знаний
"контекст синхронизации" (SynchronizationContext) — это класс, который предоставляет возможность управлять способом, которым операции переключаются обратно в основной поток или контекст для продолжения выполнения после асинхронной операции. Это важно для приложений с графическим пользовательским интерфейсом, таких как Windows Forms и WPF, где доступ к элементам пользовательского интерфейса разрешён только из основного потока.
🚩Как он работает
Абстрагирует модель синхронизации для различных сред выполнения. Например, в приложениях Windows Forms и WPF управление элементами UI должно происходить в главном потоке.
SynchronizationContext предоставляет методы для отправки (Send) и постановки (Post) задач, которые должны выполняться в правильном контексте.🟠Send
синхронно отправляет делегат на выполнение в контекст синхронизации.
🟠Post
асинхронно отправляет делегат на выполнение в контекст синхронизации.
🚩Применение SynchronizationContext
Используется для того, чтобы после асинхронной операции вернуться в правильный поток и безопасно обновить UI или выполнить код, который требует выполнения в определённом потоке.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LoadDataAsync();
}
private async void LoadDataAsync()
{
string data = await GetDataAsync();
// Асинхронно получаем данные и обновляем UI
Dispatcher.Invoke(() => DisplayData(data));
}
private Task<string> GetDataAsync()
{
return Task.Run(() =>
{
// Имитация долгой операции
Thread.Sleep(5000);
return "Data loaded";
});
}
private void DisplayData(string data)
{
MyTextBox.Text = data;
}
}
🚩Зачем он нужен
🟠Безопасность потоков
Позволяет безопасно обращаться к элементам UI из асинхронных или вторичных потоков.
🟠Правильное управление потоками
Обеспечивает выполнение кода в контексте, для которого он предназначен, что особенно важно в многопоточных и сетевых приложениях.
Ставь 👍 и забирай 📚 Базу знаний
🤔 Является ли POST идемпотентным?
Нет, метод POST не является идемпотентным. Каждый повторный вызов POST может создавать новые ресурсы или изменять состояние системы, что приводит к разным результатам. Это типичный метод для операций создания или изменения, и его повторение может вызвать дублирование или непредсказуемый эффект.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Нет, метод POST не является идемпотентным. Каждый повторный вызов POST может создавать новые ресурсы или изменять состояние системы, что приводит к разным результатам. Это типичный метод для операций создания или изменения, и его повторение может вызвать дублирование или непредсказуемый эффект.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Какие проблемы могут быть при многопоточности и как их избежать?
Многопоточное программирование позволяет улучшить производительность программ за счет параллельной обработки данных, но это также влечет за собой ряд специфических проблем. Понимание этих проблем и способы их предотвращения или управления ими критически важны для создания надежных и эффективных многопоточных приложений.
🚩Основные проблемы
🟠Гонки данных (Race Conditions)
Проблема: Два или более потоков пытаются одновременно изменить общие данные или один поток читает данные во время их изменения другим потоком, что приводит к непредсказуемым результатам.
Решение: Использование механизмов синхронизации, таких как блокировки (locks), мьютексы (mutexes) и семафоры (semaphores), для контроля доступа к общим ресурсам.
🟠Взаимная блокировка (Deadlock)
Проблема: Два или более потоков бесконечно ожидают ресурсы, заблокированные друг другом, в результате чего они не могут продолжить выполнение.
Решение: Разработка программы таким образом, чтобы потоки запрашивали ресурсы всегда в одном и том же порядке, использование таймаутов для блокировок, чтобы потоки могли выйти из состояния ожидания.
🟠Голодание (Starvation)
Проблема: Один или несколько потоков не могут получить доступ к необходимым ресурсам, потому что другие потоки постоянно занимают их.
Решение: Применение справедливых блокировок (fair locks) или алгоритмов планирования, которые обеспечивают всем потокам равный доступ к ресурсам.
🟠Переключение контекста (Context Switching)
Проблема: Частое переключение контекста между потоками может значительно снизить производительность системы, особенно если потоки часто блокируются и разблокируются.
Решение: Оптимизация количества потоков, уменьшение зависимостей между потоками и уменьшение использования блокировок.
🟠Проблемы с проектированием
Проблема: Неправильное проектирование многопоточной архитектуры может привести к сложностям в поддержке и расширении программного обеспечения.
Решение: Использование абстракций высокого уровня для работы с потоками, таких как пулы потоков, параллельные библиотеки (например, TPL в .NET) и модели акторов.
Избегание взаимной блокировки
Ставь 👍 и забирай 📚 Базу знаний
Многопоточное программирование позволяет улучшить производительность программ за счет параллельной обработки данных, но это также влечет за собой ряд специфических проблем. Понимание этих проблем и способы их предотвращения или управления ими критически важны для создания надежных и эффективных многопоточных приложений.
🚩Основные проблемы
🟠Гонки данных (Race Conditions)
Проблема: Два или более потоков пытаются одновременно изменить общие данные или один поток читает данные во время их изменения другим потоком, что приводит к непредсказуемым результатам.
Решение: Использование механизмов синхронизации, таких как блокировки (locks), мьютексы (mutexes) и семафоры (semaphores), для контроля доступа к общим ресурсам.
🟠Взаимная блокировка (Deadlock)
Проблема: Два или более потоков бесконечно ожидают ресурсы, заблокированные друг другом, в результате чего они не могут продолжить выполнение.
Решение: Разработка программы таким образом, чтобы потоки запрашивали ресурсы всегда в одном и том же порядке, использование таймаутов для блокировок, чтобы потоки могли выйти из состояния ожидания.
🟠Голодание (Starvation)
Проблема: Один или несколько потоков не могут получить доступ к необходимым ресурсам, потому что другие потоки постоянно занимают их.
Решение: Применение справедливых блокировок (fair locks) или алгоритмов планирования, которые обеспечивают всем потокам равный доступ к ресурсам.
🟠Переключение контекста (Context Switching)
Проблема: Частое переключение контекста между потоками может значительно снизить производительность системы, особенно если потоки часто блокируются и разблокируются.
Решение: Оптимизация количества потоков, уменьшение зависимостей между потоками и уменьшение использования блокировок.
🟠Проблемы с проектированием
Проблема: Неправильное проектирование многопоточной архитектуры может привести к сложностям в поддержке и расширении программного обеспечения.
Решение: Использование абстракций высокого уровня для работы с потоками, таких как пулы потоков, параллельные библиотеки (например, TPL в .NET) и модели акторов.
private static readonly object _lock = new object();
private static int _sharedResource;
public static void UpdateResource()
{
lock (_lock)
{
_sharedResource++;
// Выполнение некоторой работы с общим ресурсом
}
}
Избегание взаимной блокировки
private static readonly object _lock1
= new object();
private static readonly object _lock2 = new object();
public static void Method1()
{
lock (_lock1)
{
// Некоторые действия
lock (_lock2)
{
// Дополнительные действия
}
}
}
public static void Method2()
{
lock (_lock1)
{
// Аналогичные действия
lock (_lock2)
{
// Дополнительные действия
}
}
}
Ставь 👍 и забирай 📚 Базу знаний
🤔 Всегда ли значимые типы находятся в стеке?
Нет, значимые типы (структуры) обычно хранятся в стеке, но если они являются частью объекта ссылочного типа, то хранятся в куче. Это зависит от контекста их использования.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Нет, значимые типы (структуры) обычно хранятся в стеке, но если они являются частью объекта ссылочного типа, то хранятся в куче. Это зависит от контекста их использования.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥3
🤔 Можно ли generic методы использовать не в generic классах?
Да, можно! Обобщённые (
🚩Как это работает?
Пример: Обобщённый метод в обычном классе
Ставь 👍 и забирай 📚 Базу знаний
Да, можно! Обобщённые (
generic) методы могут существовать в обычных (не-generic) классах. 🚩Как это работает?
Generic-метод — это метод, у которого тип параметра задаётся при вызове, даже если сам класс не является обобщённым.Пример: Обобщённый метод в обычном классе
public class Utils
{
public static void Print<T>(T value) // Обобщённый метод
{
Console.WriteLine($"Тип: {typeof(T)}, Значение: {value}");
}
}
class Program
{
static void Main()
{
Utils.Print(100); // Тип: System.Int32, Значение: 100
Utils.Print("Hello"); // Тип: System.String, Значение: Hello
Utils.Print(3.14); // Тип: System.Double, Значение: 3.14
}
}
Ставь 👍 и забирай 📚 Базу знаний
🤔 Все ли HTTP-методы идемпотентные?
Нет.
Вот классификация:
Идемпотентные:
- GET — многократный вызов не меняет состояние.
- PUT — задаёт конкретное состояние ресурса.
- DELETE — может быть вызван многократно с тем же результатом.
- HEAD, OPTIONS — тоже идемпотентны.
Неидемпотентные:
- POST — каждый вызов может создавать новый ресурс или изменять состояние (непредсказуемо).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Вот классификация:
Идемпотентные:
- GET — многократный вызов не меняет состояние.
- PUT — задаёт конкретное состояние ресурса.
- DELETE — может быть вызван многократно с тем же результатом.
- HEAD, OPTIONS — тоже идемпотентны.
Неидемпотентные:
- POST — каждый вызов может создавать новый ресурс или изменять состояние (непредсказуемо).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
💊2
🤔 Что такое инверсия зависимостей?
Инверсия зависимостей — это принцип SOLID, который говорит:
> Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
Это значит, что вместо жёстких зависимостей на конкретные классы, код должен работать через абстракции (
🚩Проблема без инверсии зависимостей
Допустим, у нас есть класс
🚩Решение: Инверсия зависимостей
Чтобы избавиться от жёсткой зависимости, вводим абстракцию (
Теперь
Теперь мы можем подставлять любую реализацию
Вывод
🚩Преимущества инверсии зависимостей
Гибкость – можно легко заменять зависимости.
Тестируемость – можно подставить Mock-объект вместо
Меньше изменений в коде – можно добавить новые способы отправки сообщений без изменения
Ставь 👍 и забирай 📚 Базу знаний
Инверсия зависимостей — это принцип SOLID, который говорит:
> Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
Это значит, что вместо жёстких зависимостей на конкретные классы, код должен работать через абстракции (
interface или abstract class). 🚩Проблема без инверсии зависимостей
Допустим, у нас есть класс
EmailSender, который отправляет письма:public class EmailSender
{
public void Send(string message)
{
Console.WriteLine($"Отправка email: {message}");
}
}
public class NotificationService
{
private EmailSender _emailSender = new EmailSender();
public void Notify(string message)
{
_emailSender.Send(message);
}
}
🚩Решение: Инверсия зависимостей
Чтобы избавиться от жёсткой зависимости, вводим абстракцию (
IMessageSender):public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine($"Отправка email: {message}");
}
}
public class SmsSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine($"Отправка SMS: {message}");
}
}
Теперь
NotificationService зависит не от конкретного класса, а от интерфейса:public class NotificationService
{
private readonly IMessageSender _messageSender;
public NotificationService(IMessageSender messageSender)
{
_messageSender = messageSender;
}
public void Notify(string message)
{
_messageSender.Send(message);
}
}
Теперь мы можем подставлять любую реализацию
IMessageSender: var emailNotifier = new NotificationService(new EmailSender());
emailNotifier.Notify("Привет через Email!");
var smsNotifier = new NotificationService(new SmsSender());
smsNotifier.Notify("Привет через SMS!");
Вывод
Отправка email: Привет через Email!
Отправка SMS: Привет через SMS!
🚩Преимущества инверсии зависимостей
Гибкость – можно легко заменять зависимости.
Тестируемость – можно подставить Mock-объект вместо
EmailSender. Меньше изменений в коде – можно добавить новые способы отправки сообщений без изменения
NotificationService.Ставь 👍 и забирай 📚 Базу знаний
🤔 В чем разница между асинхронностью и многопоточностью?
Асинхронность позволяет выполнять задачи без блокировки основного потока, в то время как многопоточность предполагает разделение кода на несколько потоков. Асинхронные операции выполняются в том же потоке, но не блокируют его, а многопоточность использует параллельные потоки для выполнения задач одновременно.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Может ли абстрактный метод быть вне абстрактного класса?
Нет, абстрактный метод не может существовать вне абстрактного класса. В C# это ограничение встроено в язык, чтобы поддерживать строгую логику наследования и полиморфизма.
🚩Почему абстрактный метод требует абстрактного класса?
Абстрактный метод — это метод, который объявлен, но не имеет реализации. Он выступает как "контракт", который обязан быть реализован в производном классе.
Абстрактные методы имеют следующие особенности:
Они не могут содержать тело (реализацию).
Их цель — заставить производные классы реализовать конкретную функциональность.
Они всегда принадлежат абстрактным классам.
🚩Пример ошибки
Если попытаться объявить абстрактный метод в обычном классе, компилятор выдаст ошибку
🚩Логика дизайна
C# требует, чтобы абстрактные методы находились только в абстрактных классах, потому что:
Абстрактные классы могут содержать как абстрактные, так и обычные методы. Это позволяет определять базовое поведение в абстрактном классе и оставлять специфическую реализацию производным классам.
Обычные классы не могут содержать абстрактные методы, так как они предназначены для создания объектов, а объект не может содержать "незавершённый" метод.
Ставь 👍 и забирай 📚 Базу знаний
Нет, абстрактный метод не может существовать вне абстрактного класса. В C# это ограничение встроено в язык, чтобы поддерживать строгую логику наследования и полиморфизма.
🚩Почему абстрактный метод требует абстрактного класса?
Абстрактный метод — это метод, который объявлен, но не имеет реализации. Он выступает как "контракт", который обязан быть реализован в производном классе.
public abstract class Shape
{
public abstract double CalculateArea();
}
Абстрактные методы имеют следующие особенности:
Они не могут содержать тело (реализацию).
Их цель — заставить производные классы реализовать конкретную функциональность.
Они всегда принадлежат абстрактным классам.
🚩Пример ошибки
Если попытаться объявить абстрактный метод в обычном классе, компилятор выдаст ошибку
public class RegularClass
{
public abstract void SomeMethod(); // Ошибка: абстрактный метод может быть только в абстрактном классе
}
🚩Логика дизайна
C# требует, чтобы абстрактные методы находились только в абстрактных классах, потому что:
Абстрактные классы могут содержать как абстрактные, так и обычные методы. Это позволяет определять базовое поведение в абстрактном классе и оставлять специфическую реализацию производным классам.
Обычные классы не могут содержать абстрактные методы, так как они предназначены для создания объектов, а объект не может содержать "незавершённый" метод.
Ставь 👍 и забирай 📚 Базу знаний
🤔 В чем разница Redis и Memcached?
Redis — это больше, чем просто кэш: он поддерживает множество структур данных (строки, списки, множества, хэши). Memcached предназначен исключительно для кэширования строк и объектов, проще в использовании. Redis также обеспечивает персистентность данных, чего нет в Memcached.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔1
🤔 Что такое value-type?
Value Type (тип значения) – это тип данных, который хранит своё значение непосредственно в памяти (обычно в стеке), а не ссылку на объект в куче (heap), как это делает ссылочный тип (Reference Type).
🚩Какие типы данных являются Value Type?
Простые типы (
Структуры (
Перечисления (
Nullable-значения (
🚩Пример Value Type
🚩Где хранятся Value Type?
Value Type обычно хранятся в стеке (stack) – это быстрая область памяти.
Если структура (
🚩Когда использовать Value Type?
Если данные небольшие и часто изменяются –
Если объект недолговечный и не требует сложного поведения
Если производительность важна – Value Type быстрее из-за работы в стеке
Ставь 👍 и забирай 📚 Базу знаний
Value Type (тип значения) – это тип данных, который хранит своё значение непосредственно в памяти (обычно в стеке), а не ссылку на объект в куче (heap), как это делает ссылочный тип (Reference Type).
🚩Какие типы данных являются Value Type?
Простые типы (
int, double, bool, char, byte, float, decimal, etc.) Структуры (
struct) Перечисления (
enum) Nullable-значения (
int?, double?) 🚩Пример Value Type
int a = 10;
int b = a; // Значение копируется
b = 20;
Console.WriteLine(a); // 10 (остался неизменным)
Console.WriteLine(b); // 20
🚩Где хранятся Value Type?
Value Type обычно хранятся в стеке (stack) – это быстрая область памяти.
Если структура (
struct) является частью объекта (который хранится в куче), то её значение хранится внутри объекта в куче. struct Point
{
public int X;
public int Y;
}
class Program
{
static void Main()
{
Point p1 = new Point { X = 5, Y = 10 };
Point p2 = p1; // Копирование структуры (создаётся новый экземпляр)
p2.X = 20;
Console.WriteLine(p1.X); // 5 (остался неизменным)
Console.WriteLine(p2.X); // 20
}
}
🚩Когда использовать Value Type?
Если данные небольшие и часто изменяются –
struct Если объект недолговечный и не требует сложного поведения
Если производительность важна – Value Type быстрее из-за работы в стеке
Ставь 👍 и забирай 📚 Базу знаний
🔥1💊1
🤔 Какие виды связей бывают у join`ов?
Связи бывают: один к одному, один ко многим, многие ко многим. Они определяют, как таблицы взаимодействуют друг с другом через ключи.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
💊1
🤔 Какие типы данных можно использовать у Dictionary в качестве ключа?
В C# ключ (
Лучше всего использовать неизменяемые (
Примитивные типы (
Кортежи (
Неизменяемые структуры (
🚩Какие типы подходят в качестве ключа?
Числовые типы (
Можно использовать несколько значений в качестве ключа:
🚩Какие типы нельзя использовать в качестве ключа?
Нужно переопределить
Ставь 👍 и забирай 📚 Базу знаний
В C# ключ (
TKey) в Dictionary<TKey, TValue> должен быть уникальным и поддерживать сравнение. Лучше всего использовать неизменяемые (
immutable) типы, такие как: Примитивные типы (
int, string, char, bool, Guid, enum) Кортежи (
Tuple, ValueTuple) Неизменяемые структуры (
struct, если переопределён Equals и GetHashCode)🚩Какие типы подходят в качестве ключа?
Числовые типы (
int, double, long)var dict = new Dictionary<int, string>
{
{1, "Один"},
{2, "Два"}
};
Console.WriteLine(dict[1]); // Вывод: Один
string (лучший выбор)var dict = new Dictionary<string, int>
{
{"apple", 10},
{"banana", 5}
};
Console.WriteLine(dict["apple"]); // 10
Guid (уникальные идентификаторы)var dict = new Dictionary<Guid, string>
{
{Guid.NewGuid(), "User1"},
{Guid.NewGuid(), "User2"}
};
enum (хороший вариант)enum Status { New, Processing, Completed }
var dict = new Dictionary<Status, string>
{
{Status.New, "Заказ создан"},
{Status.Processing, "Заказ в обработке"}
};Можно использовать несколько значений в качестве ключа:
var dict = new Dictionary<(int, string), string>
{
{(1, "apple"), "Красное яблоко"},
{(2, "banana"), "Жёлтый банан"}
};
Console.WriteLine(dict[(1, "apple")]); // Красное яблоко
🚩Какие типы нельзя использовать в качестве ключа?
List<T> (и другие изменяемые коллекции)var dict = new Dictionary<List<int>, string>(); // Ошибка при использовании в качестве ключа!
class, если не переопределён Equals и GetHashCodeclass Person { public string Name; }
var dict = new Dictionary<Person, string>(); // Плохо!Нужно переопределить
Equals и GetHashCodeclass Person
{
public string Name { get; }
public Person(string name) => Name = name;
public override bool Equals(object? obj)
{
return obj is Person other && Name == other.Name;
}
public override int GetHashCode() => Name.GetHashCode();
}
Ставь 👍 и забирай 📚 Базу знаний
🤔 Какие типы данных можно использовать у Dictionary в качестве ключа?
Ключи должны быть:
- Уникальными
- Иметь устойчивую реализацию GetHashCode() и Equals()
Подходящие типы:
- Примитивы (int, string, GUID и т.п.)
- Структуры (например, кастомные value types)
- Объекты (если правильно переопределены Equals и GetHashCode)
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Ключи должны быть:
- Уникальными
- Иметь устойчивую реализацию GetHashCode() и Equals()
Подходящие типы:
- Примитивы (int, string, GUID и т.п.)
- Структуры (например, кастомные value types)
- Объекты (если правильно переопределены Equals и GetHashCode)
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥1