C# | Вопросы собесов
5.1K subscribers
36 photos
1 file
987 links
Download Telegram
📌 Какие есть виды полиморфизмов?

💬 Спрашивают в 22% собеседований

Полиморфизм в объектно-ориентированном программировании (ООП) — это способность объектов разного типа обрабатывать данные через единый интерфейс. В C# существует несколько видов полиморфизма, которые можно разделить на две основные категории: компиляторный (статический) полиморфизм и временный (динамический) полиморфизм. Рассмотрим каждый из них более подробно.

🤔 Компиляторный (статический) полиморфизм

Этот тип полиморфизма определяется на этапе компиляции. К нему относятся:

1️⃣ Перегрузка методов (Method Overloading):

Это возможность создавать несколько методов с одним и тем же именем, но с разными параметрами (типами и/или количеством параметров).

Пример:
     class MathOperations
{
public int Add(int a, int b)
{
return a + b;
}

public double Add(double a, double b)
{
return a + b;
}
}


2️⃣ Перегрузка операторов (Operator Overloading):

Позволяет определять поведение стандартных операторов (например, +, -, *, /) для пользовательских типов (классов или структур).

Пример:
     class Complex
{
public double Real { get; set; }
public double Imaginary { get; set; }

public static Complex operator +(Complex c1, Complex c2)
{
return new Complex { Real = c1.Real + c2.Real, Imaginary = c1.Imaginary + c2.Imaginary };
}
}


🤔 Временный (динамический) полиморфизм

Этот тип полиморфизма определяется на этапе выполнения программы. К нему относятся:

1️⃣ Переопределение методов (Method Overriding):

Позволяет подклассу предоставлять специфическую реализацию метода, который уже определен в его суперклассе.

Использует ключевые слова virtual в базовом классе и override в производном классе.

Пример:
     class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal sound");
}
}

class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Bark");
}
}


2️⃣ Интерфейсы (Interfaces):

Позволяют разным классам реализовывать одно и то же поведение через контракт (интерфейс).

Пример:
     interface IShape
{
void Draw();
}

class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing Circle");
}
}

class Square : IShape
{
public void Draw()
{
Console.WriteLine("Drawing Square");
}
}


🤔 Примеры использования полиморфизма

🤔 Перегрузка методов (статический полиморфизм)
class Printer
{
public void Print(string message)
{
Console.WriteLine(message);
}

public void Print(int number)
{
Console.WriteLine(number);
}
}

class Program
{
static void Main()
{
Printer printer = new Printer();
printer.Print("Hello, world!");
printer.Print(123);
}
}


🤔 Переопределение методов (динамический полиморфизм)
class Program
{
static void Main()
{
Animal myDog = new Dog();
myDog.MakeSound(); // Output: Bark
}
}


🤔 Интерфейсы (динамический полиморфизм)
class Program
{
static void Main()
{
IShape circle = new Circle();
IShape square = new Square();

circle.Draw(); // Output: Drawing Circle
square.Draw(); // Output: Drawing Square
}
}


🤔 Краткий ответ

Полиморфизм в C# бывает двух видов: статический и динамический. Статический включает перегрузку методов и операторов, а динамический включает переопределение методов и использование интерфейсов. Полиморфизм позволяет использовать единый интерфейс для работы с объектами разных типов.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3🤔1
📌 Какие есть принципы SOLID?

💬 Спрашивают в 22% собеседований

Принципы SOLID — это пять основных принципов объектно-ориентированного проектирования и программирования, которые помогают создавать гибкие и поддерживаемые системы:

1️⃣ S - Single Responsibility Principle (Принцип единственной ответственности)

2️⃣ O - Open/Closed Principle (Принцип открытости/закрытости)

3️⃣ L - Liskov Substitution Principle (Принцип подстановки Барбары Лисков)

4️⃣ I - Interface Segregation Principle (Принцип разделения интерфейса)

5️⃣ D - Dependency Inversion Principle (Принцип инверсии зависимостей)

1️⃣ Принцип единственной ответственности (Single Responsibility Principle)

Каждый класс должен иметь одну задачу и только одну причину для изменения.

Упрощает понимание, сопровождение и тестирование кода.

Пример:
public class Report
{
public string Text { get; set; }

public void PrintReport()
{
// Логика печати отчета
}
}

public class ReportSaver
{
public void SaveToFile(Report report, string filePath)
{
// Логика сохранения отчета в файл
}
}


2️⃣ Принцип открытости/закрытости (Open/Closed Principle)

Программные сущности должны быть открыты для расширения, но закрыты для модификации.

Позволяет добавлять новые функции без изменения существующего кода.

Пример:
public abstract class Shape
{
public abstract double Area();
}

public class Circle : Shape
{
public double Radius { get; set; }

public override double Area()
{
return Math.PI * Radius * Radius;
}
}

public class Square : Shape
{
public double Side { get; set; }

public override double Area()
{
return Side * Side;
}
}


3️⃣ Принцип подстановки Барбары Лисков (Liskov Substitution Principle)

Объекты подклассов должны заменять объекты базового класса без нарушения корректности программы.

Обеспечивает корректное использование наследования.

Пример:
public class Bird
{
public virtual void Fly()
{
// Логика полета
}
}

public class Sparrow : Bird
{
public override void Fly()
{
// Логика полета воробья
}
}

public class Ostrich : Bird
{
public override void Fly()
{
throw new NotImplementedException();
}
}


Здесь принцип нарушен, так как страус не может летать. Лучше не наследовать страуса от класса Bird, если метод Fly не применим ко всем подклассам.

4️⃣ Принцип разделения интерфейса (Interface Segregation Principle)

Клиенты не должны зависеть от интерфейсов, которые они не используют.

Улучшает модульность и уменьшает зависимость между компонентами.

Пример:
public interface IPrinter
{
void Print();
}

public interface IScanner
{
void Scan();
}

public class MultiFunctionPrinter : IPrinter, IScanner
{
public void Print()
{
// Логика печати
}

public void Scan()
{
// Логика сканирования
}
}

public class SimplePrinter : IPrinter
{
public void Print()
{
// Логика печати
}
}


5️⃣ Принцип инверсии зависимостей (Dependency Inversion Principle)

Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба типа модулей должны зависеть от абстракций.

Улучшает тестируемость и уменьшает связанность компонентов.

Пример:
public interface IMessageSender
{
void SendMessage(string message);
}

public class EmailSender : IMessageSender
{
public void SendMessage(string message)
{
// Логика отправки email
}
}

public class Notification
{
private readonly IMessageSender _messageSender;

public Notification(IMessageSender messageSender)
{
_messageSender = messageSender;
}

public void Send(string message)
{
_messageSender.SendMessage(message);
}
}


🤔 Краткий ответ

Принципы SOLID помогают создавать гибкие и поддерживаемые системы. Это принципы единственной ответственности, открытости/закрытости, подстановки Лисков, разделения интерфейса и инверсии зависимостей. Они направлены на улучшение структуры и качества кода.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🤯1
📌 Какие минусы у микросервисов?

💬 Спрашивают в 22% собеседований

Микросервисная архитектура предлагает множество преимуществ, таких как независимая разработка и развёртывание компонентов, повышенная масштабируемость и устойчивость к сбоям. Однако, несмотря на все эти плюсы, существуют и некоторые недостатки, которые необходимо учитывать при принятии решения о переходе на микросервисную архитектуру.

1️⃣ Сложность управления

Описание:

Микросервисная архитектура подразумевает наличие множества независимых сервисов, каждый из которых может разрабатываться, развёртываться и масштабироваться отдельно. Это приводит к увеличению числа компонентов, которыми нужно управлять.

Проблемы:

Трудность в мониторинге и управлении состоянием множества сервисов.

Усложнение оркестрации и деплоя (развёртывания).

2️⃣ Межсервисное взаимодействие

Описание:

Микросервисы взаимодействуют друг с другом по сети, используя HTTP, gRPC или другие протоколы.

Проблемы:

Возрастают сетевые задержки и накладные расходы на сериализацию/десериализацию данных.

Возможность возникновения сетевых ошибок, что требует дополнительных механизмов для их обработки и повторных попыток.

3️⃣ Сложность тестирования

Описание:

Тестирование микросервисных систем становится более сложным по сравнению с монолитными приложениями.

Проблемы:

Необходимость тестирования взаимодействий между сервисами.

Сложность воссоздания тестовой среды, которая точно отражает производственную.

4️⃣ Управление данными и консистентность

Описание:

В микросервисной архитектуре каждый сервис может иметь собственную базу данных, что помогает в разделении данных, но создаёт новые проблемы.

Проблемы:

Сложность поддержания консистентности данных между различными сервисами.

Необходимость реализации распределённых транзакций или механизмов компенсации.

5️⃣ Повышенные требования к инфраструктуре

Описание:

Микросервисы часто требуют более сложной инфраструктуры для управления контейнерами, оркестрацией, мониторингом и логированием.

Проблемы:

Увеличение затрат на инфраструктуру и её поддержку.

Необходимость в новых инструментах и технологиях, таких как Kubernetes, для эффективного управления контейнерами.

6️⃣ Организационные сложности

Описание:

Микросервисная архитектура требует изменения подхода к разработке и эксплуатации ПО, а также организационных изменений.

Проблемы:

Необходимость переквалификации команды разработчиков и операционной команды.

Потребность в новой культуре взаимодействия между командами (DevOps, CI/CD).

🤔 Пример сложностей

Предположим, у нас есть система микросервисов для интернет-магазина. Один микросервис отвечает за управление товарами, другой за обработку заказов, а третий за платежи. В монолитной архитектуре все компоненты находятся в одном приложении, и взаимодействие между ними происходит напрямую через вызовы методов. В микросервисной архитектуре взаимодействие будет происходить через сетевые запросы.

Межсервисное взаимодействие: Если сервис обработки заказов должен получить информацию о товаре, ему нужно сделать сетевой запрос к сервису управления товарами. Это может привести к сетевым задержкам или временным сбоям, если сервис управления товарами недоступен.

Консистентность данных: Когда заказ обрабатывается, сервис заказов и сервис платежей должны быть синхронизированы, чтобы избежать ситуаций, когда заказ оплачен, но не обработан должным образом. Это требует реализации механизмов для поддержания консистентности между этими сервисами.

🤔 Краткий ответ

Микросервисная архитектура может усложнить управление системой, увеличить сетевые задержки, усложнить тестирование и поддержание консистентности данных, повысить требования к инфраструктуре и создать организационные сложности.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍3
🤔 Какой модификатор доступа позволяет доступ к члену класса только изнутри самого класса и производных классов?
Anonymous Quiz
14%
private
74%
protected
10%
internal
1%
public
👍1
📌 Что такое .NET Core?

💬 Спрашивают в 22% собеседований

.NET Core — это кроссплатформенная, высокопроизводительная и модульная среда выполнения, разработанная компанией Microsoft. Она предназначена для создания современных приложений, которые могут быть развёрнуты на различных операционных системах, включая Windows, macOS и Linux. .NET Core является частью более широкой экосистемы .NET и представляет собой значительное развитие по сравнению с традиционным .NET Framework. Рассмотрим основные особенности и компоненты .NET Core подробнее.

🤔 Основные особенности .NET Core

1️⃣ Кроссплатформенность:

.NET Core поддерживает работу на различных операционных системах: Windows, macOS и Linux.

Это позволяет разработчикам писать код, который может быть запущен на любом поддерживаемом устройстве или сервере, без необходимости вносить изменения в код.

2️⃣ Высокая производительность:

.NET Core оптимизирован для высокой производительности и эффективности.

Его использование позволяет создавать высоконагруженные приложения, такие как веб-сервисы и микросервисы.

3️⃣ Модульная архитектура:

.NET Core предоставляет возможность установки только тех пакетов и библиотек, которые необходимы для конкретного приложения.

Это уменьшает объём занимаемого пространства и улучшает управляемость зависимостями.

4️⃣ Открытый исходный код:

Исходный код .NET Core является открытым и доступен на GitHub.

Это способствует прозрачности разработки и позволяет сообществу вносить вклад в улучшение платформы.

5️⃣ Унифицированная платформа:

.NET Core объединяет несколько различных платформ (например, ASP.NET для веб-разработки, Xamarin для мобильной разработки и т.д.) под единым фреймворком.

Это позволяет разработчикам использовать одни и те же инструменты и библиотеки для различных типов приложений.

🤔 Компоненты .NET Core

1️⃣ CLR (Common Language Runtime):

Среда выполнения, которая управляет исполнением .NET приложений. Включает в себя управление памятью, выполнение кода, компиляцию JIT (Just-In-Time) и сборку мусора.

2️⃣ Библиотека классов (Base Class Library, BCL):

Набор библиотек, предоставляющих основные функциональные возможности, такие как работа с файловой системой, сетевое взаимодействие, управление потоками и многое другое.

3️⃣ CLI (Command-Line Interface):

Интерфейс командной строки, который предоставляет инструменты для создания, компиляции и развёртывания приложений .NET Core.

4️⃣ SDK (Software Development Kit):

Набор инструментов и библиотек для разработки приложений на .NET Core. Включает компиляторы, библиотеку классов и инструменты для работы с проектами.

5️⃣ ASP.NET Core:

Фреймворк для разработки веб-приложений и веб-API на .NET Core. Поддерживает разработку высокопроизводительных, масштабируемых и облачно-ориентированных веб-приложений.

🤔 Пример создания простого приложения на .NET Core

Для создания простого консольного приложения на .NET Core можно использовать следующие команды CLI:

1️⃣ Установка .NET Core SDK:

Загрузите и установите .NET Core SDK с официального сайта [Microsoft .NET](https://dotnet.microsoft.com/download).

2️⃣ Создание нового проекта:
   dotnet new console -n MyConsoleApp
cd MyConsoleApp


3️⃣ Редактирование кода:

Откройте файл Program.cs и внесите изменения:
   using System;

namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, .NET Core!");
}
}
}


4️⃣ Запуск приложения:
   dotnet run


🤔 Краткий ответ

.NET Core — это кроссплатформенная, высокопроизводительная и модульная среда выполнения, созданная Microsoft. Она поддерживает Windows, macOS и Linux, позволяет создавать современные приложения и является частью экосистемы .NET.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
🤔 Какой модификатор доступа позволяет доступ только внутри того же класса?
Anonymous Quiz
1%
Public
9%
Protected
85%
Private
5%
Internal
👀5
📌 Что такое куча?

💬 Спрашивают в 22% собеседований

В программировании куча (heap) — это область памяти, которая используется для динамического распределения памяти. В отличие от стека, где память выделяется и освобождается автоматически при вызове и завершении функций, куча позволяет разработчику вручную контролировать выделение и освобождение памяти. В контексте C# и .NET, куча имеет свои особенности, связанные с управлением памятью и сборкой мусора (Garbage Collection).

🤔 Основные характеристики кучи

1️⃣ Динамическое распределение памяти:

Куча используется для хранения объектов, которые создаются в процессе выполнения программы с помощью оператора new.

Память в куче выделяется и освобождается динамически, в отличие от статически выделенной памяти на стеке.

2️⃣ Управление памятью:

Память, выделенная в куче, не освобождается автоматически при выходе из функции или блока кода. Разработчик должен явно освобождать её, если это необходимо (в языках с ручным управлением памятью, таких как C и C++).

В C# и .NET, освобождение памяти в куче осуществляется автоматически с помощью сборщика мусора.

3️⃣ Сборка мусора (Garbage Collection):

В .NET среде управляемая куча используется для хранения объектов, и память управляется сборщиком мусора.

Сборщик мусора автоматически освобождает память, занимаемую объектами, которые больше не используются приложением.

🤔 Пример использования кучи в C#

Когда вы создаете новый объект в C#, он размещается в управляемой куче. Рассмотрим простой пример:
class Program
{
static void Main()
{
Person person = new Person();
person.Name = "John";
Console.WriteLine(person.Name);
}
}

class Person
{
public string Name { get; set; }
}


В этом примере:

1️⃣ Person person = new Person(); — создается новый объект типа Person, и память для этого объекта выделяется в куче.

2️⃣ Сборщик мусора автоматически освобождает память, занимаемую объектом Person, когда он больше не нужен (например, после завершения метода Main).

🤔 Сравнение с стеком

1️⃣ Стек (Stack):

Память выделяется автоматически при вызове функции и освобождается при выходе из неё.

Используется для хранения значимых типов (например, int, float, структуры) и указателей на объекты.

Быстрое выделение и освобождение памяти, но ограниченное пространство.

2️⃣ Куча (Heap):

Память выделяется и освобождается динамически.

Используется для хранения объектов и ссылочных типов (например, классов).

Более гибкое управление памятью, но медленное выделение и освобождение по сравнению со стеком.

🤔 Преимущества и недостатки кучи

Преимущества:

Гибкость в управлении памятью, позволяет создавать объекты, срок жизни которых выходит за пределы текущего блока кода или функции.

Подходит для хранения больших объёмов данных и объектов, размер которых неизвестен во время компиляции.

Недостатки:

Более медленное выделение и освобождение памяти по сравнению со стеком.

Возможность утечек памяти в языках с ручным управлением памятью (не относится к C# благодаря сборке мусора).

Фрагментация памяти, которая может возникнуть из-за частого выделения и освобождения различных объектов.

🤔 Краткий ответ

Куча — это область памяти, используемая для динамического распределения памяти объектов в процессе выполнения программы. В C# управление памятью в куче осуществляется автоматически с помощью сборщика мусора. Куча предоставляет гибкость, но может быть менее эффективной по сравнению со стеком.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Какая из концепций ООП описывает способность метода работать с объектами разных типов?
Anonymous Quiz
6%
Наследование
72%
Полиморфизм
4%
Инкапсуляция
19%
Абстракция
📌 Где мы используем интерфейсы?

💬 Спрашивают в 22% собеседований

Интерфейсы в C# используются для определения контрактов, которые классы должны реализовать, и играют важную роль в создании гибких и масштабируемых приложений. Вот основные случаи их использования:

🤔 Основные случаи использования интерфейсов

1️⃣ Определение контрактов:

Интерфейсы определяют набор методов и свойств, которые классы должны реализовать, обеспечивая консистентность.

Пример:
     public interface ILogger
{
void Log(string message);
}

public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}


2️⃣ Поддержка полиморфизма:

Интерфейсы позволяют обрабатывать объекты разных классов одинаково, если они реализуют один и тот же интерфейс.

Пример:
     public interface IShape
{
double Area();
}

public class Circle : IShape
{
public double Radius { get; set; }
public double Area() => Math.PI * Radius * Radius;
}

public class Square : IShape
{
public double Side { get; set; }
public double Area() => Side * Side;
}


3️⃣ Ослабление связей (Decoupling):

Интерфейсы помогают ослабить связи между компонентами, делая систему более модульной и легко модифицируемой.

Пример:
     public interface IDataAccess
{
void SaveData(string data);
}

public class DatabaseAccess : IDataAccess
{
public void SaveData(string data)
{
// Сохранение данных в базу данных
}
}

public class FileAccess : IDataAccess
{
public void SaveData(string data)
{
// Сохранение данных в файл
}
}

public class DataService
{
private readonly IDataAccess _dataAccess;

public DataService(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}

public void Save(string data)
{
_dataAccess.SaveData(data);
}
}


4️⃣ Поддержка множественного наследования:

Классы могут реализовывать несколько интерфейсов, что позволяет реализовать множественное наследование поведения.

Пример:
     public interface IFlyable
{
void Fly();
}

public interface IWalkable
{
void Walk();
}

public class Bird : IFlyable, IWalkable
{
public void Fly()
{
// Логика полета
}

public void Walk()
{
// Логика ходьбы
}
}


5️⃣ Инъекция зависимостей (Dependency Injection):

Интерфейсы часто используются в паттерне инъекции зависимостей для внедрения зависимостей и улучшения тестируемости кода.

Пример:
     public interface IEmailService
{
void SendEmail(string to, string subject, string body);
}

public class EmailService : IEmailService
{
public void SendEmail(string to, string subject, string body)
{
// Логика отправки email
}
}

public class Notification
{
private readonly IEmailService _emailService;

public Notification(IEmailService emailService)
{
_emailService = emailService;
}

public void NotifyUser(string userEmail)
{
_emailService.SendEmail(userEmail, "Notification", "You have a new notification.");
}
}


🤔 Краткий ответ

Интерфейсы используются для определения контрактов, поддержки полиморфизма, ослабления связей между компонентами, реализации множественного наследования, инъекции зависимостей и упрощения тестирования. Они помогают создать гибкую и поддерживаемую архитектуру приложений.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍52🤯1
🤔 Какой метод LINQ используется для получения пересечения двух последовательностей?
Anonymous Quiz
21%
Union
49%
Intersect
8%
Concat
22%
Join
👀5
📌 Что такое мультикаст-делегат?

💬 Спрашивают в 22% собеседований

Мультикаст-делегат в C# - это тип делегата, который может ссылаться на несколько методов. Это позволяет вызывать несколько методов в одном вызове делегата. Такая возможность полезна для выполнения последовательности действий, таких как уведомление нескольких подписчиков о событии.

🤔 Основные понятия

1️⃣ Делегат: Делегат - это тип, который определяет сигнатуру метода и может ссылаться на любой метод с совместимой сигнатурой. Делегаты похожи на указатели на функции в C++, но являются типобезопасными и встроены в систему типов C#.

2️⃣ Мультикаст-делегат: Это делегат, который может содержать список методов. Когда вызывается мультикаст-делегат, все методы в его списке вызываются в порядке их добавления.

🤔 Почему это нужно

Уведомление нескольких объектов: Мультикаст-делегаты широко используются в системе событий .NET. Например, когда происходит какое-то событие (например, изменение состояния объекта), все подписчики на это событие могут быть уведомлены через мультикаст-делегат.

Легкость добавления и удаления методов: Методы могут быть легко добавлены или удалены из списка вызовов делегата, что делает их гибким инструментом для динамического управления логикой программы.

🤔 Использование мультикаст-делегатов

Создадим простой пример, где мультикаст-делегат используется для уведомления нескольких методов:
using System;

public class Program
{
// Определяем делегат, который принимает строку и ничего не возвращает
public delegate void Notify(string message);

// Метод, который соответствует сигнатуре делегата
public static void SendEmail(string message)
{
Console.WriteLine($"Email sent: {message}");
}

// Другой метод, который соответствует сигнатуре делегата
public static void LogMessage(string message)
{
Console.WriteLine($"Log entry: {message}");
}

public static void Main()
{
// Создаем экземпляр делегата и добавляем методы
Notify notifyDel = SendEmail;
notifyDel += LogMessage;

// Вызываем делегат
notifyDel("System update available.");

// Удаляем метод из делегата
notifyDel -= LogMessage;

// Вызываем делегат снова
notifyDel("Security patch applied.");
}
}


🤔 Объяснение кода

1️⃣ Определение делегата: Мы объявляем делегат Notify, который принимает строку в качестве параметра и не возвращает значение.

2️⃣ Создание методов: Два метода SendEmail и LogMessage, которые соответствуют сигнатуре делегата Notify.

3️⃣ Создание экземпляра делегата: Мы создаем экземпляр делегата и добавляем методы в список вызовов.

4️⃣ Вызов делегата: Когда мы вызываем делегат, он вызывает все методы в своем списке.

5️⃣ Удаление метода из делегата: Мы удаляем метод LogMessage из списка вызовов делегата.

🤔 Краткий ответ

Мультикаст-делегат - это делегат, который может ссылаться на несколько методов и вызывать их последовательно. Это полезно для выполнения нескольких действий в одном вызове, таких как уведомление нескольких подписчиков о событии.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31
🤔 Какое ключевое слово в C# используется для объявления блока кода, который гарантированно выполнится после завершения блока try?
Anonymous Quiz
86%
finally
12%
catch
0%
dispose
1%
using
📌 Что такое IoC-контейнеры?

💬 Спрашивают в 22% собеседований

IoC (Inversion of Control) контейнеры – это фреймворки или библиотеки, которые управляют созданием и внедрением зависимостей в приложении. Они помогают реализовать принцип внедрения зависимостей (Dependency Injection), что делает код более гибким, тестируемым и легко расширяемым.

🤔 Основные понятия

1️⃣ Inversion of Control (IoC): IoC – это принцип, при котором управление объектами и их зависимостями передается внешней системе или фреймворку, вместо того чтобы объекты самостоятельно управляли своими зависимостями.

2️⃣ Dependency Injection (DI): DI – это метод внедрения зависимостей, при котором зависимости (объекты, от которых зависит другой объект) предоставляются внешним источником (например, IoC-контейнером) вместо того, чтобы объект создавал их самостоятельно.

🤔 Почему это нужно

Упрощение тестирования: Легко заменять зависимости на заглушки или моки, что облегчает тестирование.

Снижение связности кода: Объекты не зависят от конкретных реализаций своих зависимостей, а зависят от абстракций (интерфейсов), что упрощает изменение и расширение кода.

Централизованное управление зависимостями: IoC-контейнеры управляют жизненным циклом объектов и их зависимостей, что делает код более организованным и понятным.

🤔 Пример использования IoC-контейнера

Рассмотрим простой пример использования популярного IoC-контейнера Autofac в C#:

1️⃣ Установка Autofac: Добавляем пакет Autofac через NuGet Package Manager.

2️⃣ Создание интерфейсов и классов:
public interface ILogger
{
void Log(string message);
}

public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}

public interface IService
{
void Serve();
}

public class Service : IService
{
private readonly ILogger _logger;

public Service(ILogger logger)
{
_logger = logger;
}

public void Serve()
{
_logger.Log("Service Called");
}
}


3️⃣ Настройка и использование IoC-контейнера:
using Autofac;

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();

// Регистрируем зависимости
builder.RegisterType<ConsoleLogger>().As<ILogger>();
builder.RegisterType<Service>().As<IService>();

// Строим контейнер
var container = builder.Build();

// Разрешаем зависимости
var service = container.Resolve<IService>();
service.Serve();
}
}


🤔 Объяснение кода

1️⃣ Интерфейсы и реализации: Мы создаем интерфейсы ILogger и IService, а также их реализации ConsoleLogger и Service.

2️⃣ Регистрация зависимостей: С помощью ContainerBuilder регистрируем классы ConsoleLogger и Service как реализации соответствующих интерфейсов.

3️⃣ Разрешение зависимостей: С помощью контейнера разрешаем зависимости и вызываем метод Serve у объекта Service, который использует ConsoleLogger для логирования.

🤔 Краткий ответ

IoC-контейнеры управляют созданием и внедрением зависимостей в приложении, упрощая тестирование и организацию кода. Они позволяют регистрировать и разрешать зависимости, делая код более гибким и легко расширяемым.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой из следующих методов C# используется для явного указания, что объект должен быть собран сборщиком мусора?
Anonymous Quiz
64%
Dispose()
12%
SuppressFinalize()
22%
Collect()
2%
Abort()
👾1
📌 Что такое принцип подстановки Барбары Лисков?

💬 Спрашивают в 22% собеседований

Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP) - это один из пяти принципов SOLID, который гласит, что объекты подтипов должны быть заменяемы объектами базового типа без нарушения правильности работы программы. Это означает, что если класс B является подтипом класса A, то объекты класса A могут быть заменены объектами класса B без изменения желаемого поведения программы.

🤔 Основные понятия

1️⃣ Наследование: Подтипы должны наследовать не только интерфейс, но и поведение базовых типов.

2️⃣ Замена: Класс-наследник должен корректно работать в любом контексте, где ожидается объект базового класса.

3️⃣ Контракт: Подтип должен соблюдать контракт базового типа, что включает предусловия, постусловия и инварианты.

🤔 Почему это нужно

Упрощение понимания и поддержки кода: Код, который следует LSP, более предсказуем и понятен, что облегчает его поддержку.

Повышение надежности: Соблюдение принципа помогает избежать неожиданных ошибок при замене объектов базового класса на объекты подклассов.

Расширяемость: Принцип способствует созданию гибкой и расширяемой архитектуры, в которой новые подтипы можно добавлять без риска нарушения существующего кода.

🤔 Пример нарушения LSP

Рассмотрим пример, где нарушение LSP приводит к некорректному поведению программы:
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }

public int GetArea()
{
return Width * Height;
}
}

public class Square : Rectangle
{
public override int Width
{
set { base.Width = base.Height = value; }
}

public override int Height
{
set { base.Width = base.Height = value; }
}
}


🤔 Проблема

В этом примере Square (квадрат) наследует от Rectangle (прямоугольник). Однако, квадрат имеет особое свойство: его ширина и высота всегда равны. При попытке установить ширину или высоту квадрата, изменяется и другое значение, что нарушает поведение прямоугольника.

🤔 Исправление

Лучше использовать композицию вместо наследования для избежания такой проблемы:
public interface IShape
{
int GetArea();
}

public class Rectangle : IShape
{
public int Width { get; set; }
public int Height { get; set; }

public int GetArea()
{
return Width * Height;
}
}

public class Square : IShape
{
public int SideLength { get; set; }

public int GetArea()
{
return SideLength * SideLength;
}
}


🤔 Объяснение

1️⃣ Интерфейс IShape: Определяем общий интерфейс для всех форм.

2️⃣ Реализация прямоугольника и квадрата: Классы Rectangle и Square реализуют интерфейс IShape, но теперь они не наследуются друг от друга и имеют свои собственные свойства и методы.

🤔 Краткий ответ

Принцип подстановки Лисков гласит, что объекты подтипов должны быть заменяемы объектами базового типа без нарушения работы программы. Это необходимо для повышения предсказуемости, надежности и расширяемости кода.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5👾1
🤔 Какое ключевое слово в C# используется для создания метода, который может быть переопределён в производном классе?
Anonymous Quiz
73%
virtual
14%
abstract
12%
override
1%
sealed
📌 Какие колекции знаешь?

💬 Спрашивают в 22% собеседований

В C# существует множество коллекций, которые предоставляются в стандартной библиотеке .NET. Они делятся на несколько категорий, каждая из которых предназначена для различных сценариев использования. Вот основные типы коллекций:

1️⃣ Коллекции общего назначения

List<T>: Динамический массив, который автоматически изменяет свой размер по мере добавления элементов.
  List<int> numbers = new List<int> { 1, 2, 3, 4 };
numbers.Add(5);


Dictionary<TKey, TValue>: Коллекция пар ключ-значение. Обеспечивает быстрый доступ к значению по ключу.
  Dictionary<string, int> ages = new Dictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};
int aliceAge = ages["Alice"];


HashSet<T>: Коллекция уникальных элементов. Используется для проверки наличия элемента и предотвращения дублирования.
  HashSet<string> fruits = new HashSet<string> { "Apple", "Banana" };
fruits.Add("Apple"); // Не добавит дубликат


Queue<T>: Очередь, работающая по принципу FIFO (First In, First Out).
  Queue<string> queue = new Queue<string>();
queue.Enqueue("first");
queue.Enqueue("second");
string item = queue.Dequeue(); // "first"


Stack<T>: Стек, работающий по принципу LIFO (Last In, First Out).
  Stack<string> stack = new Stack<string>();
stack.Push("first");
stack.Push("second");
string item = stack.Pop(); // "second"


2️⃣ Специализированные коллекции

LinkedList<T>: Двусвязный список, позволяющий быстро вставлять и удалять элементы.
  LinkedList<int> linkedList = new LinkedList<int>();
linkedList.AddLast(1);
linkedList.AddLast(2);


SortedList<TKey, TValue>: Коллекция пар ключ-значение, отсортированная по ключам.
  SortedList<string, int> sortedList = new SortedList<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};


SortedDictionary<TKey, TValue>: Похож на SortedList, но использует бинарное дерево для хранения элементов.
  SortedDictionary<string, int> sortedDict = new SortedDictionary<string, int>
{
{ "Alice", 30 },
{ "Bob", 25 }
};


SortedSet<T>: Отсортированное множество уникальных элементов.
  SortedSet<int> sortedSet = new SortedSet<int> { 3, 1, 2 };


3️⃣ Коллекции для параллельного программирования

ConcurrentDictionary<TKey, TValue>: Параллельная версия Dictionary, предназначенная для безопасного использования в многопоточных приложениях.
  ConcurrentDictionary<string, int> concurrentDict = new ConcurrentDictionary<string, int>();
concurrentDict.TryAdd("Alice", 30);


ConcurrentQueue<T>: Параллельная версия Queue.
  ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>();
concurrentQueue.Enqueue("first");


ConcurrentStack<T>: Параллельная версия Stack.
  ConcurrentStack<string> concurrentStack = new ConcurrentStack<string>();
concurrentStack.Push("first");


BlockingCollection<T>: Коллекция, поддерживающая операции добавления и извлечения с блокировкой и ограничением по емкости.
  BlockingCollection<int> blockingCollection = new BlockingCollection<int>(5);
blockingCollection.Add(1);


🤔 Краткий ответ

В C# есть множество коллекций для различных целей, включая List, Dictionary, HashSet, Queue, Stack, специализированные коллекции, такие как LinkedList, SortedList, а также коллекции для параллельного программирования, такие как ConcurrentDictionary и BlockingCollection.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
📌 Что такое DI(инъекция зависимости)?

💬 Спрашивают в 22% собеседований

Dependency Injection (DI), или инъекция зависимости, — это шаблон проектирования, который позволяет объектам получить свои зависимости от внешних источников, а не создавать их самостоятельно. Этот подход улучшает тестируемость, расширяемость и поддерживаемость кода, делая его более гибким и модульным.

🤔 Основные концепции

1️⃣ Зависимость: Любой объект или класс, от которого зависит другой объект или класс. Например, если класс A использует класс B для выполнения своих задач, то B является зависимостью для A.

2️⃣ Инъекция зависимости: Процесс предоставления зависимости объекту извне, обычно через конструктор, метод или свойство.

🤔 Преимущества DI

Улучшенная тестируемость: Легко заменять реальные зависимости на моки или заглушки, что упрощает модульное тестирование.

Ослабленная связность: Классы не жестко связаны с конкретными реализациями своих зависимостей, что упрощает их замену и изменение.

Расширяемость и поддерживаемость: Позволяет легко добавлять новые функциональные возможности и изменять существующие зависимости.

🤔 Виды инъекций зависимости

1️⃣ Конструкторная инъекция: Зависимости передаются через параметры конструктора.
   public class Service
{
private readonly IRepository _repository;

public Service(IRepository repository)
{
_repository = repository;
}

public void DoWork()
{
_repository.Save();
}
}


2️⃣ Инъекция через свойства: Зависимости устанавливаются через свойства класса.
   public class Service
{
public IRepository Repository { get; set; }

public void DoWork()
{
Repository.Save();
}
}


3️⃣ Инъекция через методы: Зависимости передаются через параметры метода.
   public class Service
{
private IRepository _repository;

public void SetRepository(IRepository repository)
{
_repository = repository;
}

public void DoWork()
{
_repository.Save();
}
}


🤔 Использование DI с IoC-контейнерами

IoC-контейнеры (контейнеры инверсии управления) управляют созданием и жизненным циклом зависимостей, а также автоматизируют процесс их инъекции. Пример использования популярного IoC-контейнера Autofac:

1️⃣ Установка Autofac: Добавляем пакет Autofac через NuGet Package Manager.

2️⃣ Настройка контейнера и регистрация зависимостей:
   using Autofac;

var builder = new ContainerBuilder();
builder.RegisterType<Repository>().As<IRepository>();
builder.RegisterType<Service>().As<IService>();
var container = builder.Build();


3️⃣ Разрешение зависимостей и использование сервиса:
   using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<IService>();
service.DoWork();
}


🤔 Краткий ответ

Dependency Injection (DI) — это метод предоставления зависимостей объекту извне, что делает код более гибким, тестируемым и поддерживаемым. DI может осуществляться через конструкторы, свойства или методы, а IoC-контейнеры автоматизируют процесс управления зависимостями.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Какой модификатор доступа в C# используется для обеспечения доступа к члену класса только внутри его текущего сборочного контекста?
Anonymous Quiz
18%
private
9%
protected
61%
internal
13%
protected internal
📌 В чем разница между списком и массивом?

💬 Спрашивают в 22% собеседований

В C# списки (List) и массивы (Array) являются двумя основными типами коллекций, но они имеют различия в своих свойствах, возможностях и сценариях использования. Рассмотрим основные различия между ними.

🤔 Массивы (Array)

1️⃣ Размер: Массивы имеют фиксированный размер, который необходимо указать при создании. Размер массива не может быть изменен после его создания.
   int[] numbers = new int[5]; // Массив из 5 элементов


2️⃣ Тип данных: Массивы являются типобезопасными, что означает, что все элементы массива должны быть одного типа.
   string[] words = new string[] { "apple", "banana", "cherry" };


3️⃣ Быстродействие: Массивы обеспечивают очень быстрый доступ к элементам по индексу, так как они хранятся в непрерывном блоке памяти.
   int firstNumber = numbers[0]; // Быстрый доступ по индексу


4️⃣ Методы и свойства: Массивы имеют ограниченный набор методов и свойств. В основном это методы для работы с элементами, такие как Length.
   int length = numbers.Length; // Длина массива


🤔 Списки (List)

1️⃣ Размер: Списки имеют динамический размер, который автоматически изменяется по мере добавления или удаления элементов.
   List<int> numbersList = new List<int> { 1, 2, 3, 4, 5 };
numbersList.Add(6); // Добавление элемента увеличивает размер списка


2️⃣ Тип данных: Списки также являются типобезопасными и могут хранить элементы только одного типа.
   List<string> wordsList = new List<string> { "apple", "banana", "cherry" };


3️⃣ Быстродействие: Доступ к элементам списка по индексу также быстрый, но из-за динамической природы список может быть менее эффективен по памяти при частом изменении размера.
   int firstNumberInList = numbersList[0]; // Быстрый доступ по индексу


4️⃣ Методы и свойства: Списки обладают богатым набором методов и свойств, таких как Add, Remove, Find, Count и многие другие, что делает их более удобными для работы с динамическими данными.
   int count = numbersList.Count; // Количество элементов в списке
numbersList.Remove(3); // Удаление элемента


🤔 Основные различия

1️⃣ Размер: Массивы фиксированного размера, списки динамического размера.

2️⃣ Гибкость: Списки более гибкие и предоставляют больше методов для работы с элементами.

3️⃣ Производительность: Массивы могут быть более эффективны по памяти и быстрее при доступе к элементам, но менее гибкие в изменении размера.

🤔 Пример использования

Массив:
int[] numbersArray = new int[] { 1, 2, 3, 4, 5 };
int firstElement = numbersArray[0];
numbersArray[2] = 10;
Console.WriteLine(numbersArray.Length);


Список:
List<int> numbersList = new List<int> { 1, 2, 3, 4, 5 };
numbersList.Add(6);
int firstElementInList = numbersList[0];
numbersList[2] = 10;
Console.WriteLine(numbersList.Count);
numbersList.Remove(4);


🤔 Краткий ответ

Массивы имеют фиксированный размер и обеспечивают быстрый доступ к элементам, но менее гибкие. Списки имеют динамический размер и предоставляют больше методов для работы с элементами, но могут быть менее эффективны по памяти.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6