Комментарии 10
Полная иммутабельность, увы, не дана нам прямо из коробки.
А как же init аксессор появившийся в C# 9.0? Следующий код позволит проинициализировать свойства при создании объекта/структуры, но при попытке изменить свойство внутри или снаружи объекта еще перед компиляцией покажет что это невозможно.
namespace cicd_test
{
public class SomeClass
{
public int MyProperty { get; init; }
// Этот метод не даст приложению собраться
public void SetProp(int i) => MyProperty = i;
}
public struct SomeStruct
{
public int MyProperty { get; init; }
// Этот метод не даст приложению собраться
public void SetProp(int i) => MyProperty = i;
}
public class Test
{
// Тут все нормально
SomeClass someClass = new()
{
MyProperty = 0
};
// Тоже покажет ошибку и не даст скомпилироваться
someClass.MyProperty = 1;
// Тут все нормально
SomeStruct someStruct = new()
{
MyProperty = 0
};
// Тоже покажет ошибку и не даст скомпилироваться
someStruct.MyProperty = 1;
}
}
В более ранних версиях можно явно использовать readonly поля, а в свойстве только get.
А вообще насколько я понимаю в .NET языках иммутабельность не значит, что нельзя менять значения и ай-яй-яй. Это значит, что при вызове изменения - создаётся и возвращается новое значение.
Имхо, задача гораздо лучше решается с помощью кастомного Roslyn Analyzer: находим все классы, помеченные неким маркерным атрибутом IImmutable
, пробегаемся по списку их полей и проверяем типы (возможно рекурсивно).
В отличие от решения в статье, анализатор:
- Будет работать статически на этапе компиляции
- Не потребует написания кошмарного кода вида
MethodBase.GetCurrentMethod().Name.Substring(4)
В чем сложность определить ImmutableClass?
public class SimpleImmutableClass
{
public SimpleImmutableClass(
string firstName, string lastName, IEnumerable<string> items
)
{
FirstName = firstName;
LastName = lastName;
Items = items.ToList();
}
//Неявные приватные сеттеры
public string LastName { get; }
public string FirstName { get; }
public IReadOnlyList<string> Items { get; }
}
А как же record?
Статью писал человек совсем далёкий от Net.
Все примеры кривые, для коллекций например есть IReadOnlyCollection. Есть как верно подмечено инициаторы.
Да и всё уже давно придумано за нас.
То-есть написать класс с конструктором и приватными сеттерами это ой-ой-ой какая жуть, а наогородить кучу кода с рефлексией это респектос?
Подход конечно огонь!!!!
Если уж рефлексируете то хоть кешируйте результаты, по секрету скажу у одного и того же типа обьёктов полей в рантайме не прибавиться))))
А что мешает возвращать в геттере коллекцию .AsReadOnly()?
Я бы не назвал этот подход "изящным решением".
Имхо гораздо проще реализовать иммутабельность через конструктор с приватными сеттерами, если это допустим доменная сущность и или объект-значение, либо вообще без сеттеров если это DTO.
Иммутабельность в C#