Мысли о Первичных Конструкторах. Начало
См. также Введение в Первичные Конструкторы в C#12.
Лучшие варианты использования
Рассмотрим случаи, для которых первичные конструкторы хорошо подходят.
1. Базовая инициализация поля
Первичные конструкторы сокращают объём кода, который вам нужно написать, и вместо этого компилятор генерирует этот код за вас:
public class Person(string firstName, string lastName)
{
private readonly string _firstName = firstName;
private readonly string _lastName = lastName;
}
Однако, даже добавление валидации уже делает код не таким очевидным:
public class Person(string firstName, string lastName)
{
private readonly string _firstName =
!string.IsNullOrEmpty(firstName)
? firstName
: throw new ArgumentException(
"Must not be null or empty", nameof(firstName));
…
}
Вы не можете использовать защитные методы, доступные в .NET 7+:
ArgumentException.ThrowIfNullOrEmpty(firstName);
и уже одно это делает код стандартного конструктора более сжатым и понятным. Понятно, что можно создать методы-помощники и сократить количество кода, продолжая использовать первичные конструкторы, но зачем?
2. Инициализация в тестовом коде
Возьмем простой пример теста xunit для типа Person:
public class PersonTests(ITestOutputHelper output)
{
[Theory]
[InlineData("Jon", "")]
public void Person_throws_if_null_or_empty(
string firstName, string lastName)
{
output.WriteLine(
$"Testing '{firstName}' and '{lastName}'");
Assert.Throws<ArgumentException>(() =>
new Person(firstName, lastName));
}
}
Опустим тут ценность этого теста, и нужно ли нам использовать ITestOutputHelper в методе. Здесь интересно то, что добавление ITestOutputHelper в качестве параметра первичного конструктора упрощает тестовый класс. Для теста не имеет значения, хранится ли ITestOutputHelper в поле или нет, и является ли он изменяемым.
3. Внедрение зависимостей в контроллерах MVC
В ASP.NET Core, вы, вероятно, используете внедрение зависимостей и не часто проверяете зависимости, которые внедряются в контроллеры. Это делает их хорошими кандидатами для первичных конструкторов (можно даже использовать значения напрямую):
public class MyController(
ILogger<MyController> logger,
IService service) : ControllerBase
{
[HttpGet]
public ActionResult<Thing> Get()
{
logger.LogInformation("Get the thing");
return service.GetThing();
}
}
Очевидно, что это коротко и ясно. Но имейте в виду, что теперь зависимости изменяемы, и им можно назначать значения в теле контроллера, даже если вы, скорее всего, никогда не захотите этого делать.
Фактически, вы, вероятно, обнаружите, что большая часть ежедневного кода, который вы пишете в своём приложении, может использовать первичные конструкторы без какого-либо существенного влияния, кроме сокращения строк кода.
Тем не менее, при использовании первичных конструкторов есть вещи, на которые стоит обратить внимание.
Окончание следует…
Источник: https://andrewlock.net/thoughts-about-primary-constructors-3-pros-and-5-cons/