Уютное сообщество C# разработчиков
2.52K subscribers
44 photos
53 links
Уютное сообщество C# - обучающий канал для разработчиков.

Полезные материалы по языку программирования.
Тесты на знание C#
Подсказки и трюки языка

@aldrson @viktorreh
加入频道
Что такое .NET?

.NET это фреймворк (платформа), написанный компанией Microsoft для создания множества разных типов приложений. Почему фреймворк назвали именно так, до сих пор до конца не ясно.

.NET позволяет работать со множеством языков - C#, F#, Visual Basic. C#, как вы поняли самый популярный.

На сегодняшний день, современным видением платформы то, что выросло из .NET Core. То есть, курс на кросс-платформенность и отсутствие привязки к операционной системе Windows. Тут уже и приложения быстрее, и можно собрать приложение из микросервисов в Docker контейнерах.

Что можно сделать с помощью .NET?
Веб, мобилки, десктоп, ML, геймдев, IoT и так далее.
Но люди все равно выберут Java, поэтому пожелаем роста платформе!

Если хотите узнать больше о платформе .NET, поделитесь этим в комментариях!

#полезное #tips
👍16🔥1
Freeze/Inject/Register в AutoFixture

Давным-давно, в первых версиях библиотеки балом правил Register. Два других метода ещё не существовали.

Определён он был следующим образом:

public static void Register<T>(this IFixture fixture, T item)

Однако, была и перегрузка:

public static void Register<T>(this IFixture fixture, Func<T> creator)

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

fixture.Register(() => universe.LightUp());

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

fixture.Register(universe.LightUp);

Что если universe.LightUp это обращение к свойству, а не к методу? Тогда была бы выбрана первая перегрузка.

Но это крайне неочевидно.
Поэтому первая перегрузка превратилась в Inject<T>(this IFixture fixture, T item).

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

var foo = fixture.Create<Foo>();
fixture.Inject(foo);

Тогда, автор библиотеки решил, что это так называемая концепция заморозки.
В итоге два вызова превратились в один метод Freeze.

#полезное #tips
Какую самую главную ошибку совершают новички в ООП?

Начав изучать ООП, многие знакомятся с его тремя столпами: инкапсуляция, наследование и полиморфизм. Но удаётся ли правильно понять все из них?

Если выполнить поиск определения инкапсуляции в Google, то можно получить примерно следующее:

«Инкапсуляция в программировании — это принцип, согласно которому внутреннее устройство сущностей нужно объединять в специальной «оболочке» и скрывать от вмешательств извне.»

А теперь вспомним, что самыми популярными объектно-ориентированными языками программирования являются Java и C#, где модификаторы доступа (private, public, protected, etc.) это одни из самых используемых ключевых слов. И получается, что начинающие программисты путают сокрытие с инкапсуляцией.

На самом же деле, корректнее привести следующее определение:

«Инкапсуляция – это объединение в рамках одной структуры функций и данных, с которыми эти функции работают.»

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

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

Вот простой пример, чтобы понять разницу:

class Doubler
{
private readonly int value;

public Doubler(int value) => this.value = value;

public int Double() => this.value * 2;
}

//…

public int Double(int value) => value * 2;

#полезное #tips
16👍2
Многоликая регистрация в стандартном контейнере

Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:

public interface IBar {}
public interface IFoo {}

public class FooBar : IFoo, IBar {}


Наша задача зарегистировать экземпляр FooBar таким образом, чтобы при внедрении зависимостей IBar и IFoo использовался один и тот же объект.

Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры FooBar:

services.AddSingleton<IFoo, FooBar>();
services.AddSingleton<IBar, FooBar>();


Но проблема вполне решаема, достаточно лишь зарегистрировать вначале экземпляр FooBar и затем ссылаться на него:

services.AddSingleton<FooBar>();
services.AddSingleton<IFoo>(x => x.GetRequiredService<FooBar>());
services.AddSingleton<IBar>(x => x.GetRequiredService<FooBar>());

#полезное #tips
👍31
Autofac. Именованные сервисы

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

Например, у нас есть сервис IDisplay, отображающий какие-то произведения искусства IArtwork.
Чтобы указать, что мы хотим внедрить конкретную реализацию MyPainting, можно использовать атрибут KeyFilterAttribute.
По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.

Пример:


public class ArtDisplay : IDisplay
{
public ArtDisplay([KeyFilter("MyPainting")] IArtwork art) { ... }
}

// ...

var builder = new ContainerBuilder();

builder.RegisterType<MyPainting>().Keyed<IArtwork>("MyPainting");
builder.RegisterType<ArtDisplay>().As<IDisplay>().WithAttributeFiltering();

// ...
var container = builder.Build();

#полезное #tips
4👍2🤔2❤‍🔥1
Знали ли вы...

Что в .NET 7 при разработке API больше не требуется явно указывать атрибут [FromServices] для зависимостей, указанных в параметре метода?

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

Соответственно, такой код спокойно отработает без ошибок:

Services.AddScoped<SomeCustomType>();

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
public ActionResult Get(SomeCustomType service) => Ok();
}

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

Services.Configure<ApiBehaviorOptions>(options =>
{
options.DisableImplicitFromServicesParameters = true;
})

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

#полезное #tips
👍13😨2
Как изменить таймаут для конкретного запроса в HttpClient?

Одной из лучших практик по работе с HttpClient в C# считается переиспользование одного экземпляра клиента для множества запросов. Как минимум во избежание port exhaustion.

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

Проблема в том, что HttpClient.Timeout устанавливается единожды, во время создания клиента. И несмотря на наличие public set'тера, это значение не может быть изменено впоследствии. Любые попытки пресекаются выбрасыванием InvalidOperationException.

Но я бы не писал этот пост, если бы не существовало решения проблемы. А решение довольно простое:
TimeSpan timeout = GetMyTimeout();
using (var tokenSource = new CancellationTokenSource(timeout))
{
var response = await httpClient.GetAsync(uri, tokenSource.Token);
HandleResponse(response);
}


Такое решение можно не только использовать "в лоб", но и обернуть в пайплайн из DelegatingHandler'ов. Для того чтобы оно работало, потребуется убедиться в двух вещах:

1️⃣ Пользовательский таймаут меньше того, что установлен в HttpClient.Timeout

2️⃣ Пользовательский таймаут валиден. Проще говоря, время ожидания больше 0 секунд.

#полезное #tips
9👍4
marker interface

Для чего их вообще используют?
Допустим, в коде используется некий объект, который реализует указанный интерфейс. Тогда, появляется возможность проверить реализуется ли он и скорректировать на основе этого обработку объекта.

Также, маркерный интерфейс может быть необходимым злом при отсутствии поддержки в языке discriminated union types.
К сожалению, в объектно-ориентированных языках вроде C# объявить тип, который будет чем-то конкретным из указанного набора, невозможно.

Поэтому, приходится прибегать к таким уловкам.

Почему маркерных интерфейсов стоит избегать?
Главная проблема такого подхода - нарушение инкапсуляции.

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

Применение маркерного интерфейса подразумевает, что где-то будет находится проверка на этот маркер. Это противоречит идее инкапсуляции, потому что у объекта появляется знание о реализации той части системы, которая находится совершенно вне зоны его «полномочий».

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

#полезное #tips
👍4
На пальцах про AAA

Как вы могли понять речь пойдёт не о батарейках или играх. Сегодняшний пост про юнит-тестирование.

Согласитесь, в таком сложном мире как программирование сложно ориентироваться, когда всё лежит где попало, не на своих местах.

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

Arrange

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

Act

Затем идёт действие. То есть, непосредственно вызов тестируемого функционала.

Assert

Финальный этап - проверка. Проверяется всё что требуется проверить. Какие получились данные, состояние объектов, вызвалось ли то что нужно, была ли ошибка. В общем, есть где развернуться.

Вот и получается, что паттерн Arrange-Act-Assert за счёт своей простоты и эффективности в отношении организации и написания тестов стал де-факто стандартом индустрии.

#полезное #tips
👍9
Do I need to run tests before push?

На текущем проекте мы используем Kafka. Так вышло, что я - MacBook enjoyer и пишу код на m1 машине.

Соответственно, интеграционные тесты, задействующие Kafka тупо не запускаются.
И какое-то время назад у меня в голове возник вопрос: «а должно ли это вообще меня волновать?»

Ладно, Kafka. Но в большом коммерческом проекте есть ещё много других вещей, которые нужно было бы поднимать на своей машине, просто чтобы запустить приложение:

▪️Эмулятор внешних систем (mock интеграций);

▪️Базы данных;

▪️Кэш;

▪️Gateway микросервисов;

И многое другое…

Зачем мне засорять компьютер, когда уже есть облако с окружением, где крутятся пайплайны, триггернутые коммитом? CI/CD - это автоматизация всей вот этой рутины. И я воспользуюсь этим технологическим достижением, чтобы упростить себе жизнь.

Смысл прогонять тесты на машине, если репорт будет читаться из пайплайна в гитлабе?

#полезное #tips
👍41🤣1
Друзья, какая из рубрик канала вам нравится больше всего?

Вакансии - #вакансия
Статьи -
#полезное
Трюки и возможности языка -
#tips
Тесты -
#тест
Книги -
#книги

Напишите в комментариях, если хотите, чтобы мы что-то добавили - тоже в комменты.
👍11👎1