C# | Вопросы собесов
5.08K subscribers
36 photos
1 file
1.32K links
Download Telegram
🤔 Может ли абстрактный метод быть вне абстрактного класса?

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

🚩Почему абстрактный метод требует абстрактного класса?

Абстрактный метод — это метод, который объявлен, но не имеет реализации. Он выступает как "контракт", который обязан быть реализован в производном классе.
public abstract class Shape
{
public abstract double CalculateArea();
}


Абстрактные методы имеют следующие особенности:
Они не могут содержать тело (реализацию).
Их цель — заставить производные классы реализовать конкретную функциональность.
Они всегда принадлежат абстрактным классам.

🚩Пример ошибки

Если попытаться объявить абстрактный метод в обычном классе, компилятор выдаст ошибку
public class RegularClass
{
public abstract void SomeMethod(); // Ошибка: абстрактный метод может быть только в абстрактном классе
}


🚩Логика дизайна

C# требует, чтобы абстрактные методы находились только в абстрактных классах, потому что:
Абстрактные классы могут содержать как абстрактные, так и обычные методы. Это позволяет определять базовое поведение в абстрактном классе и оставлять специфическую реализацию производным классам.
Обычные классы не могут содержать абстрактные методы, так как они предназначены для создания объектов, а объект не может содержать "незавершённый" метод.

Ставь 👍 и забирай 📚 Базу знаний
🤔 Можно ли в рамках lock использовать await?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1
🤔 Какие есть методы запросов жизненного цикла в asp.net Core?

В ASP.NET Core жизненный цикл запроса проходит несколько этапов — от получения HTTP-запроса до отправки ответа. В этом процессе участвуют Middleware, контроллеры, фильтры и обработчики событий.

🚩Основные этапы жизненного цикла запроса

Получение запроса (HttpContext создаётся)
Обработка через Middleware (передача запроса вниз)
Маршрутизация (Routing) — определение контроллера
Фильтры (например, аутентификация)
Вызов контроллера и метода (Action)
Обратный проход через Middleware (формирование ответа)
Отправка ответа клиенту

🚩Методы Middleware (Промежуточное ПО)

Middleware — это основной механизм обработки запросов.
Где регистрируются? → В Program.csapp.Use...)
Методы Middleware
app.Use(async (context, next) =>
{
Console.WriteLine("Перед обработкой запроса");
await next(); // Передаём запрос дальше
Console.WriteLine("После обработки запроса");
});


🚩Методы в контроллерах (`Controller`)

Контроллер обрабатывает запросы после маршрутизации.
Основные методы
public class HomeController : Controller
{
// Метод вызывается при GET-запросе
public IActionResult Index()
{
return View();
}

// Метод вызывается при POST-запросе
[HttpPost]
public IActionResult SubmitForm(FormModel model)
{
return RedirectToAction("Index");
}
}


🚩Фильтры (`Filters`)
Фильтры выполняются до и после вызова контроллера.
Методы фильтров
public class MyActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine("Перед вызовом метода контроллера");
}

public void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine("После вызова метода контроллера");
}
}


🚩Методы жизненного цикла в `Program.cs`

В ASP.NET Core 6+ вся конфигурация находится в Program.cs.
Методы инициализации
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews(); // Подключаем MVC

var app = builder.Build();

app.UseRouting(); // Включаем маршрутизацию
app.UseAuthorization(); // Проверка прав
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Подключаем контроллеры
});

app.Run();


Ставь 👍 и забирай 📚 Базу знаний
👍3
🤔 Принципы ООП?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥3
🤔 Что такое .NET стандарт?

.NET Standard — это спецификация API, которая определяет набор базовых библиотек, доступных во всех реализациях .NET (например, .NET Framework, .NET Core, Xamarin, Unity и других). Она была создана для обеспечения совместимости между разными платформами .NET.

🚩Зачем нужен .NET Standard?

До появления .NET Standard существовало несколько отдельных реализаций .NET:
.NET Framework (для Windows-приложений)
.NET Core (кроссплатформенная версия .NET)
Mono/Xamarin (для мобильных и игровых приложений)
Каждая из них имела свои особенности и набор доступных API. Из-за этого разработчики, создавая библиотеку, сталкивались с проблемой совместимости: приходилось писать несколько версий кода под разные платформы или использовать Portable Class Library (PCL), которая имела ограниченный функционал.
.NET Standard решил эту проблему, введя единый набор API, который обязаны поддерживать все реализации .NET.

🚩Как это работает?

.NET Standard представляет собой абстрактную спецификацию API, которая реализуется разными версиями .NET. Например, .NET Standard 2.0 поддерживается в .NET Framework 4.6.1, .NET Core 2.0 и выше. Если библиотека написана под .NET Standard 2.0, её можно использовать во всех этих средах.

🚩Версии .NET Standard

Существуют разные версии .NET Standard, каждая из которых включает больше API, чем предыдущая. Чем выше версия, тем больше возможностей, но и тем меньше совместимость с более старыми реализациями .NET.

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

Создаём Class Library с таргетом .NET Standard 2.0:
   namespace MyLibrary
{
public class MathHelper
{
public static int Add(int a, int b)
{
return a + b;
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое Box/Unbox и как этого можно избежать?

Boxing — это преобразование значимого типа (int, float и т.д.) в ссылочный тип (object), т.е. упаковка значения в объект.
Unboxing — это обратное преобразование: из object обратно в value-type.
Проблема: это создаёт накладные расходы на память и производительность.
Избежать можно следующим образом:
- Использовать обобщённые коллекции (List<int> вместо ArrayList).
- Не использовать object, если можно указать конкретный тип.
- Избегать приведения типов без необходимости.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1
🤔 В чем суть extension методов?

Extension-методы (методы расширения) — это способ добавить новые методы к существующим классам, не изменяя их код и не создавая наследников. Они позволяют расширять классы и интерфейсы, даже если у нас нет доступа к их исходному коду.

🚩Как работают extension-методы?

Это обычные статические методы, но объявленные внутри статического класса.
Первый параметр метода должен принимать тот тип, который мы хотим расширить, и перед ним ставится ключевое слово this.
После этого метод становится доступен как "встроенный" у этого типа.

🟠Добавляем метод к `string`
Допустим, у нас есть строка, и мы хотим добавить метод ToSnakeCase, который заменяет пробелы на нижние подчеркивания.
using System;

public static class StringExtensions
{
public static string ToSnakeCase(this string str)
{
return str.Replace(" ", "_").ToLower();
}
}

class Program
{
static void Main()
{
string text = "Hello World";
Console.WriteLine(text.ToSnakeCase()); // hello_world
}
}


🟠Расширяем `List<int>`
Добавим метод `SumEvenNumbers()`, который суммирует только четные числа в List<int>.
using System;
using System.Collections.Generic;
using System.Linq;

public static class ListExtensions
{
public static int SumEvenNumbers(this List<int> numbers)
{
return numbers.Where(n => n % 2 == 0).Sum();
}
}

class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
Console.WriteLine(numbers.SumEvenNumbers()); // 12 (2 + 4 + 6)
}
}


🚩Когда использовать extension-методы?

Когда нужно добавить новый метод к существующему классу, но нельзя изменить его код (например, string, List<T>, DateTime).
Когда хочется сделать код более читаемым: numbers.SumEvenNumbers() лучше, чем MyExtensions.SumEvenNumbers(numbers).
Когда нужно улучшить API без наследования и изменения структуры классов.

Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое String?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍4🔥1
🤔 Какие тесты бывают?

🚩Виды

🟠Юнит-тесты
Предназначены для проверки отдельных компонентов или модулей приложения в изоляции. Они помогают убедиться, что отдельные функции или методы работают правильно.
Цель: Проверка логики отдельных методов или классов.
Инструменты: xUnit, NUnit, MSTest.
using Xunit;

public class CalculatorTests
{
[Fact]
public void Add_SimpleValues_ReturnsSum()
{
var calculator = new Calculator();
var result = calculator.Add(2, 3);
Assert.Equal(5, result);
}
}

public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}


🟠Интеграционные тесты
Проверяют взаимодействие между различными компонентами системы, убеждаясь, что они корректно работают вместе.
Цель: Проверка взаимодействия между модулями.
Инструменты: xUnit, NUnit, MSTest, плюс дополнительные библиотеки для тестирования баз данных или HTTP-запросов.
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

public class IntegrationTests
{
private readonly HttpClient _client;

public IntegrationTests()
{
var appFactory = new CustomWebApplicationFactory<Startup>();
_client = appFactory.CreateClient();
}

[Fact]
public async Task Get_EndpointReturnsSuccessAndCorrectContentType()
{
var response = await _client.GetAsync("/api/values");
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());
}
}


🟠Функциональные тесты
Проверяют, что приложение выполняет свои функции в соответствии с требованиями. Эти тесты проверяют конкретные сценарии использования.
Цель: Проверка функциональности приложения на уровне пользователя.
Инструменты: Selenium, Playwright, Cypress.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using Xunit;

public class UiTests
{
[Fact]
public void LoadPage_CheckTitle()
{
using (IWebDriver driver = new ChromeDriver())
{
driver.Navigate().GoToUrl("https://example.com");
Assert.Equal("Example Domain", driver.Title);
}
}
}


🟠Системные тесты
Проверяют приложение в целом, включая взаимодействие с внешними системами и проверку всех требований.
Цель: Проверка всей системы в интегрированном виде.
Инструменты: JUnit, TestNG для Java, или те же инструменты, что и для функциональных тестов.

🟠Приемочные тесты
Проводятся для проверки, что приложение соответствует требованиям и готово к использованию клиентом или конечным пользователем.
Цель: Подтверждение соответствия приложения требованиям.
Инструменты: Cucumber, SpecFlow (для BDD).

🟠Регрессионные тесты
Проверяют, что недавние изменения в коде не нарушили существующую функциональность.
Цель: Убедиться, что новые изменения не привели к новым багам.
Инструменты: Все инструменты для юнит-тестирования и функционального тестирования.

🟠Нагрузочные тесты
Проверяют, как приложение ведет себя под нагрузкой, например, при большом количестве одновременных пользователей или операций.
Цель: Оценка производительности и устойчивости приложения под нагрузкой.
Инструменты: JMeter, Gatling, Apache Bench.

Ставь 👍 и забирай 📚 Базу знаний
🤔 Что занимает меньше памяти: класс или структура?

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥4
🤔 Что такое контекст синхронизации?

"контекст синхронизации" (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 из асинхронных или вторичных потоков.
🟠Правильное управление потоками
Обеспечивает выполнение кода в контексте, для которого он предназначен, что особенно важно в многопоточных и сетевых приложениях.

Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Действительно ли при вызове метода Add уже генерируется SQL-код?

Нет, при вызове метода Add SQL-код еще не генерируется. На этом этапе сущность просто добавляется в контекст, и ее состояние помечается как "добавленное". SQL-код формируется и выполняется только при вызове метода SaveChanges(), который анализирует все изменения и генерирует соответствующие SQL-запросы.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🤔 Что такое IEnumerable?

IEnumerable — это интерфейс в базовой библиотеке классов .NET Framework, который определяет один метод: GetEnumerator(). Этот метод возвращает объект IEnumerator, который позволяет перебирать элементы коллекции (например, массива или списка) один за другим.

🚩Зачем он нужен?

Используется для создания универсального метода перебора данных, не зависящего от типа коллекции. Это означает, что любой тип данных, который реализует IEnumerable, можно перебирать с помощью цикла foreach в C#. Это упрощает работу с различными структурами данных, предоставляя единый механизм для итерации элементов.

🚩Как он используется?

Когда вы реализуете интерфейс IEnumerable в своём классе, вы обязуете этот класс предоставлять метод GetEnumerator(), который возвращает IEnumerator. IEnumerator, в свою очередь, имеет методы для перехода к следующему элементу (MoveNext) и для получения текущего элемента (Current), а также метод Reset(), который возвращает перечислитель в начальное состояние.
using System;
using System.Collections;

public class DaysOfWeek : IEnumerable
{
private string[] days = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

public IEnumerator GetEnumerator()
{
for (int index = 0; index < days.Length; index++)
{
// Yield each day of the week.
yield return days[index];
}
}
}

public class Program
{
public static void Main()
{
DaysOfWeek daysOfWeek = new DaysOfWeek();
foreach (string day in daysOfWeek)
{
Console.WriteLine(day);
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
👍1
🤔 Какая разница работы асинхронности на C# и JavaScript?

В C#:
- Асинхронность основана на async/await, Task, ThreadPool.
- Может использоваться для как I/O (сетевые запросы), так и CPU-bound операций.
- Компилятор генерирует state-machine для управления переходами состояний задачи.
- Асинхронность может использовать потоки, но не всегда (например, при I/O — потоки не блокируются вовсе).
В JavaScript:
- Асинхронность основана на Promise, async/await.
- Однопоточная модель с event loop.
- Не используется многопоточность, даже если операции асинхронны.
- Все I/O происходят через неблокирующий механизм событий.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥1
🤔 В каком случае использовать интерфейс, в каком абстрактный класс?

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

🚩Когда использовать интерфейсы


🟠Множественное наследование
Класс может реализовывать несколько интерфейсов, но наследоваться только от одного класса. Используйте интерфейсы, когда требуется множественное наследование.
public interface IDriveable
{
void Drive();
}

public interface IFlyable
{
void Fly();
}

public class FlyingCar : IDriveable, IFlyable
{
public void Drive() { /* Реализация вождения */ }
public void Fly() { /* Реализация полета */ }
}


🟠Общее поведение без реализации
Используйте интерфейсы, чтобы определить общий набор методов и свойств без предоставления какой-либо реализации. Это позволяет разным классам реализовать интерфейс по-своему.
public interface IShape
{
double GetArea();
}

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

public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double GetArea() => Width * Height;
}


🟠Гибкость и полиморфизм
Интерфейсы позволяют легко менять реализацию и обеспечивают гибкость в коде. Можно использовать интерфейсы для создания полиморфных коллекций или методов, которые работают с разными реализациями интерфейсов.
public void DrawShapes(IEnumerable<IShape> shapes)
{
foreach (var shape in shapes)
{
Console.WriteLine($"Area: {shape.GetArea()}");
}
}


🚩Когда использовать

🟠Частичная реализация
Используйте абстрактные классы, если вы хотите предоставить некоторую общую реализацию, которую могут использовать подклассы. Абстрактные классы могут содержать как абстрактные, так и не абстрактные методы.
public abstract class Animal
{
public abstract void MakeSound();
public void Sleep() => Console.WriteLine("Sleeping");
}

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


🟠Шаблонный метод
Абстрактные классы хорошо подходят для реализации паттерна "Шаблонный метод", где общий алгоритм реализован в абстрактном классе, а конкретные шаги определены в подклассах.
public abstract class Game
{
public void Play()
{
Initialize();
StartPlay();
EndPlay();
}

protected abstract void Initialize();
protected abstract void StartPlay();
protected abstract void EndPlay();
}

public class Football : Game
{
protected override void Initialize() => Console.WriteLine("Football Game Initialized");
protected override void StartPlay() => Console.WriteLine("Football Game Started");
protected override void EndPlay() => Console.WriteLine("Football Game Ended");
}


Ставь 👍 и забирай 📚 Базу знаний
🤔 Какие ресурсы очищают Dispose и Finalize?

- Dispose используется для ручного освобождения ресурсов, таких как:
- Файлы
- Соединения с базами данных
- Сетевые сокеты
- Таймеры и обработчики событий
- Finalize вызывается автоматически сборщиком мусора, чтобы освободить неуправляемые ресурсы (например, дескрипторы ОС, ресурсы вне .NET).
Dispose — быстрее и надёжнее, потому что вызывается явно, в отличие от непредсказуемого Finalize.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥2
🤔 Как сейчас делается Singleton?

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

🟠Ленивый Singleton (Lazy Initialization)
Ленивый Singleton инициализируется при первом обращении. Это обеспечивает отложенную инициализацию объекта и гарантирует потокобезопасность.
public class Singleton
{
private static readonly Lazy<Singleton> lazyInstance = new Lazy<Singleton>(() => new Singleton());

public static Singleton Instance => lazyInstance.Value;

private Singleton()
{
// Приватный конструктор
}
}


🟠Потокобезопасный Singleton (Thread-safe)
Этот подход использует lock для обеспечения потокобезопасности при создании экземпляра.
public class Singleton
{
private static Singleton instance;
private static readonly object lockObj = new object();

private Singleton()
{
// Приватный конструктор
}

public static Singleton Instance
{
get
{
if (instance == null)
{
lock (lockObj)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}


Eager Initialization (Инициализация при загрузке)
Экземпляр Singleton создается при загрузке класса. Это гарантирует потокобезопасность за счет особенностей инициализации статических переменных в .NET.
public class Singleton
{
private static readonly Singleton instance = new Singleton();

public static Singleton Instance => instance;

private Singleton()
{
// Приватный конструктор
}
}


🟠Static Constructor (Статический конструктор)
Использование статического конструктора для инициализации Singleton.
public class Singleton
{
private static readonly Singleton instance;

static Singleton()
{
instance = new Singleton();
}

public static Singleton Instance => instance;

private Singleton()
{
// Приватный конструктор
}
}


Singleton с внедрением зависимостей (Dependency Injection)
В современных приложениях, особенно с использованием ASP.NET Core, Singleton часто регистрируется в контейнере внедрения зависимостей.
public class SingletonService
{
public void DoWork()
{
// Выполнение работы
}
}

// Регистрация в контейнере служб
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<SingletonService>();
}

// Использование в контроллере
public class MyController : ControllerBase
{
private readonly SingletonService _singletonService;

public MyController(SingletonService singletonService)
{
_singletonService = singletonService;
}

public IActionResult Index()
{
_singletonService.DoWork();
return Ok();
}
}


Ставь 👍 и забирай 📚 Базу знаний
🤔 Что такое стек?

Стек — это структура данных LIFO, где последний добавленный элемент извлекается первым, используется для вызовов функций и локальных переменных.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
👍1
🤔 Какие есть модификаторы доступа?

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

🚩Подробное объяснение с примерами

🟠`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
🤔 Что такое Inversion of control и dependency injection?

Inversion of control (IoC) — это принцип, при котором управление созданием объектов передается внешним компонентам. Dependency injection (DI) — это один из способов реализации IoC, когда зависимости передаются объекту через конструктор, методы или свойства. Это позволяет улучшить тестируемость и модульность кода. DI делает систему более гибкой, позволяя изменять реализации зависимостей без изменения кода.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
🔥1
🤔 Возможно ли как-нибудь ограничить типы, которые пользователь будет передавать через шаблон?

Можно ограничить типы, которые передаются в шаблоны (generics), с помощью ключевого слова where. Это позволяет указать, какие типы подходят для использования, обеспечивая безопасность и предсказуемость кода. Вот основные виды ограничений:

🟠Класс или структура
where T : class — только классы.
where T : struct — только структуры.

🟠Интерфейс
Указание интерфейса, который должен реализовать тип:

   public class MyClass<T> where T : IDisposable { }


🟠Базовый класс
Указание, что тип должен быть наследником определённого класса:

   public class MyClass<T> where T : Exception { }


🟠Конструктор
Ограничение на наличие конструктора без параметров:

   public class MyClass<T> where T : new() { }


🟠Комбинированные ограничения
Можно объединять несколько условий:

   public class MyClass<T> where T : class, IDisposable, new() { }


Ставь 👍 и забирай 📚 Базу знаний