.NET Разработчик
6.52K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1871. #ВопросыНаСобеседовании #ASPNET
Самые часто задаваемые вопросы на собеседовании по C#


30. Какие существуют возможности управления конфигурацией приложения .NET Core в разных средах (разработка/тестирование/производство)?

.NET Core предоставляет решение для управления конфигурациями в различных средах с помощью поставщиков конфигурации.

Во-первых, задать среду. Приложения .NET Core делают это, читая переменную окружения ASPNETCORE_ENVIRONMENT. Значение по умолчанию – "Production". Формально значение может быть любым, но лучше придерживаться одного из трёх стандартных:
- Development
- Staging
- Production
Это даст доступ, например, к методам расширения, таким как IHostingEnvironment.IsDevelopment(). Значение можно задать в переменных окружения машины. IDE, такие как Visual Studio, также могут брать это значение из файла launch.json из корня проекта.
Кроме того, среду можно задать принудительно в коде, вручную настроив веб хост:
var builder = new WebHostBuilder()
.UseEnvironment(Environments.Production)
.UseKestrel()
.… ;

Среда определяется на раннем этапе старта приложения, поэтому дальнейшую настройку можно определять в зависимости от среды. Например, для управления конфигурацией можно использовать файл appsettings.json, который является файлом по умолчанию, считываемым приложением .NET Core. Можно иметь отдельные файлы конфигурации для каждой среды, например appsettings.Development.json или appsettings.Production.json, что даёт возможность переопределить настройки по умолчанию.

Вообще есть несколько вариантов настройки:
1. Использовать различные настройки в коде в зависимости от среды, например:

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

2. Использовать различные файлы appsettings.{Environment}.json для хранения конфигураций, специфичных для среды.

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

4. Важно исключить конфиденциальные данные, такие как строки подключения и секреты приложений, из системы управления версиями (например, файлов appsettings.json). Их лучше хранить либо в переменных среды, либо использовать безопасные методы, такие как пользовательские секреты в среде разработки или сервисы вроде Azure Key Vault в производственной среде.

В приложении использовать интерфейс IConfiguration для доступа к настроенным параметрам. См. подробнее.

См. также про изменения в конфигурации введённые в .NET6.

Источники:
-
https://dev.to/bytehide/net-core-interview-question-answers-4bc1
- Эндрю Лок “
ASP.NET Core в действии”. 2-е изд. – М.: ДМК Пресс, 2021. Глава 11.
👍23
День 1910. #ВопросыНаСобеседовании #ASP.NET #Architecture
Самые часто задаваемые вопросы на собеседовании по C#

31. Как бы вы подошли к обработке ошибок и отладке в распределённом приложении .NET Core с несколькими микросервисами?

Обработка ошибок и отладка в таком приложении может оказаться сложной задачей. Можно использовать несколько стратегий:

1. Централизованные логи.
Все микросервисы должны отправлять свои логи (естественно, структурированные) в централизованное место, где они сопоставляются и индексируются. Это позволит искать и визуализировать логи всех сервисов в одном месте.

2. Использование идентификаторов корреляции.
Идентификаторы корреляции (CorrelationId) — это уникальные идентификаторы, присваиваемые запросу. Затем этот идентификатор передаётся всем сервисам, участвующим в обработке этого запроса. Это позволяет отслеживать всю цепочку запросов и ответов.

3. Проверки работоспособности.
Проверки работоспособности можно реализовать для мониторинга состояния микросервисов. Они могут сообщать такие показатели, как время безотказной работы, загрузка ЦП, использование памяти и т. д.

4. Промежуточное ПО для обработки исключений.
В архитектуре микросервисов ошибки должны обрабатываться на уровне сервиса. Каждый сервис должен обрабатывать свои собственные исключения и возвращать подходящее сообщение об ошибке или код ответа.
Можно создать промежуточное ПО, которое обрабатывает каждый запрос микросервиса. Если во время выполнения запроса возникает исключение, это промежуточное ПО перехватит исключение и ответит подходящим сообщением об ошибке.
См. также «Улучшенная Обработка Исключений в ASP.NET Core 8.»

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

Источник: https://dev.to/bytehide/net-core-interview-question-answers-4bc1
👍21
День 2445. #ВопросыНаСобеседовании
Давно не было на канале вопросов с собеседований. Смотрите все посты на эту тему по хэштэгу выше. Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.

4. Интерфейсы и абстрактные классы
«Когда в .NET следует использовать интерфейс, а когда — абстрактный класс? Приведите примеры ситуаций, в которых один вариант будет более подходящим, чем другой».

Хороший ответ
«В .NET и интерфейсы, и абстрактные классы используются для определения контрактов в коде, но их применение различается в зависимости от необходимости наследования и типа полиморфизма.

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

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

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


Часто встречающийся неудачный ответ
«Я использую абстрактный класс всякий раз, когда мне нужно определить методы, которые не следует изменять, а интерфейсы — когда мне нужно просто реализовать множество различных методов. Поэтому, если у меня есть методы, которые не следует переопределять, я помещаю их в абстрактный класс».


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

- Непонимание абстрактных классов: Абстрактные классы предназначены не только для определения методов, которые «не следует изменять». Хотя абстрактный класс действительно может содержать конкретные методы, их основное предназначение — служить базовым классом для расширения другими классами, предоставляя общий код и определяя абстрактные методы, которые должны быть реализованы подклассами. Тот факт, что они могут включать непереопределяемые методы (с помощью ключевого слова sealed в C#), является второстепенным по сравнению с их основной функцией.

- Непонимание интерфейсов: Утверждение, что интерфейсы используются «только для реализации набора различных методов», слишком упрощает их роль. Интерфейсы определяют контракт, которому следуют классы, что критически важно для разработки систем, в которых различные классы могут использоваться взаимозаменяемо, не зная их конкретных реализаций.

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍21👎2
День 2452. #ВопросыНаСобеседовании
Марк Прайс в своей книге предложил свой набор из 60 вопросов на собеседовании.

5. Свойства и индексаторы

«Можете ли вы объяснить разницу между свойствами и индексаторами в C# и привести примеры ситуаций, в которых каждый из них может быть использован?»

Хороший ответ
«В C# свойства и индексаторы используются для инкапсуляции данных, но они служат разным целям и используются в разных контекстах. Свойства действуют как комбинация метода и поля, предоставляя способ получения и установки значений с дополнительной логикой, используя синтаксис, подобный синтаксису полей. Свойства наиболее подходят, когда требуется предоставить данные класса с потенциальной проверкой, вычислением или преобразованием. Например, класс Person может иметь свойство DateOfBirth, которое гарантирует, что дата установлена в прошлом, и вычисляет возраст человека при получении.

Индексаторы позволяют индексировать объект как массив, хотя ключ может быть любого типа данных, а не только целочисленным. Это особый тип свойства, позволяющий получать доступ к классам с помощью оператора доступа к массиву []. Индексаторы особенно полезны, когда класс представляет собой коллекцию элементов. Например, класс Library может использовать индексатор, чтобы предоставить клиентам доступ к книгам по числовому индексу.

Как свойства, так и индексаторы включают методы доступа get и set (или только один из них), и оба могут включать в себя дополнительную логику в этих методах для обеспечения инкапсуляции или управления побочными эффектами.»


Часто встречающийся неточный ответ:
«Я использую свойства, когда мне нужно хранить данные в полях, и индексаторы, когда я хочу использовать массивы в своих классах».

Этот ответ отражает непонимание как свойств, так и индексаторов:

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

- Непонимание индексаторов: Индексаторы — это не просто способ реализации массивов внутри классов. Они позволяют индексировать экземпляр класса подобно массивам, но и служат для того, чтобы класс вёл себя как коллекция. Это непонимание принижает роль индексаторов в абстракции и инкапсуляции.

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

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍11
День 2459. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы). Я решил разобрать их тут. Начнём с 4го, поскольку первые 3 приведены в его книге, обзор на которую я недавно выкладывал.

6. Обобщения (дженерики)

«Можете объяснить преимущества использования обобщений в приложениях .NET и привести пример сценария, в котором использование обобщений может значительно улучшить качество и производительность кода?»

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

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

Пример сценария, в котором обобщения значительно повышают качество и производительность кода, — реализация классов-коллекций. Например, без обобщений список из целых чисел, хранил бы их как объекты (тип object), что потребовало бы упаковки. С обобщениями можно создать List<int>, в котором целые числа хранятся как целые числа, а не объекты, что устраняет необходимость в упаковке и распаковке, повышает производительность выполнения и снижает накладные расходы на память.

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


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

Этот ответ демонстрирует понимание обобщений на элементарном уровне, но упускает важные детали и преимущества:

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

- Чрезмерное упрощение: Концепция обобщений сводится просто к «работе с любым типом», что упускает из виду истинное их предназначение — обеспечение повторного использования кода и безопасности в типоспецифичных операциях.

- Непонимание реализации: В этом ответе не проводится различие между использованием обобщений для обеспечения типобезопасности и необобщённым подходом с использованием типов object, что может привести к проблемам с производительностью и не обеспечивает проверку типов во время компиляции, которую обеспечивают обобщения.

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

См. также Ковариантность и Контравариантность в Обобщениях.

Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👍7