При работе с неизменяемыми типами данных, такими как readonly struct, нам часто приходится писать методы типа или статические методы расширения, которые создают копию объекта, изменяя определенное свойство или поле. Такие методы позволяют сделать код чище и проще, обеспечивая неизменяемость. Неизменяемость может быть полезной, например, если требуется обеспечить потокобезопасность для типа с состоянием. Обычно каждый подобный метод в качестве единственного аргумента принимает значения поля или свойства, которое нужно изменить, создает неполную измененную копию текущего объекта, используя конструктор, и возвращает эту измененную копию потребителю. Например, для структуры Person: — результатом выполнения методов With… является неполная измененная копия оригинального объекта Person. Копия неполная, потому что для ссылочных свойств и полей, как в данном случае, копируется ссылка на объект. Но это не страшно, так как предполагается, что все типы этих полей или свойств также неизменяемы.  Пример использования методов типа: Статические методы расширения работают аналогично предыдущим методам, за исключением того, что они имеют еще один дополнительный аргумент для передачи оригинального объекта: Такой подход применим и к обычным неизменяемым структурам и классам.  Начиная с C# версии 9, можно использовать ключевое слово record для определения ссылочного типа. Он предоставляет встроенные возможности для инкапсуляции данных. Record позволяет создавать записи с неизменяемыми свойствами, используя позиционные параметры или стандартный синтаксис свойств: Несмотря на поддержку изменений, записи предназначены в первую очередь для неизменяемых моделей данных. Начиная с C# версии 10, можно определить типы record struct также с помощью позиционных параметров или синтаксиса свойств: Обычную запись из C# версии 9 можно обозначить более полным выражением record class, где class - это необязательное ключевое слово. Выбор между record class и record struct, соответствует выбору между между class и struct. С появлением записей появилось и новое ключевое with. Оно создает новый экземпляр записи, который является копией оригинальной и изменяет в этой копии указанные свойства и поля, результатом чего является неполная копия. Для указания требуемых изменений используется синтаксис инициализатора объектов: Синтаксис выглядит лаконичным и легко читаемым. Но существует несколько причин, по которым вспомогательные методы из первых примеров для структур и классов, оказываются предпочтительнее ключевому слову with. Во-первых, не во всех проектах можно использовать записи из-за версии C# и целевой версии фреймворка. А во-вторых, в неизменяемых типах часто встречаются поля или свойства содержащие неизменяемые коллекции объектов. Например: При работе с коллекциями использование ключевого слово with выглядит менее читаемо: По этой причине появилась идея Immutype для того, чтобы поручить ему всю рутинную работу по созданию статических методов расширения для поддержки неизменяемости. Это генератор кода .NET, который делает всю работу одновременно, пока вы пишите код. Он ищет все типы, помеченные атрибутом , и для каждого такого типа создает статический класс с методами расширения. Эти методы расширения не замусоривают основной код и не маячат своими изменениями в коммитах. Для создания копии объектов используются: позиционные параметры для записей, если таковые имеются конструктор помеченный атрибутом , если такой есть первый конструктор с наибольшим числом аргументов Например, для записи: — сценарий из примера выше, выглядит так: Для обычных свойств или полей, таких как Age создается по одному методу расширения: Для свойств или полей со значением по умолчанию в позиционном параметре или в конструкторе, таких как Name, создается дополнительный метод, назначение которого очевидно: Для коллекций создается по несколько методов расширения. В нашем случае для Friends будет создано четыре метода. Для того чтобы полностью переопределить коллекцию, через метод с переменным числом аргументов: Такой же как и выше, но с оригинальным типом в качестве аргумента: Чтобы создать копию коллекции, добавив переменное число элементов: Чтобы создать копию коллекции, исключив некоторые элементы: Контраст в простоте при использовании других типов коллекций еще более очевиден. Например, для записи: — наш сценарий с ключевым словом with выглядят так: А в случае с Immutype весь код остается прежним для любых коллекций из списка ниже: Immutype также берет на себя обработку случаев с типами, содержащими null или значения по умолчанию. Поддерживает универсальные типы и всевозможные ограничения параметров типа. Так как Immutype - это генератор кода, он работает только на этапе компиляции и не добавляет каких-либо зависимости на другие сборки, не захламляет исходный код. Для работы Immutype необходим: .NET SDK 5.0.102 или новее. Но он будет работать для разных проектов, например, для .NET Framework 4.5.  Immutype поддерживает инкрементальную генерацию кода, поэтому нагрузка на систему в процессе работы над проектами будет минимальной.  Чтобы начать пользоваться, просто добавьте в ваши проекты ссылку на пакет Immutype и отметьте неизменяемые типы атрибутом  по необходимости. С дополнительными примерами можно ознакомиться на странице проекта. Буду признателен за конструктивные комментарии, новые идеи и вклад в проект. Протестировано использование record и record struct с ключевым словом With и со статическими методами, созданными Immutype, для случаев с одним, двумя и четырьмя свойствами. Типы свойств: string, int и bool. С одним свойством string: Method Mean Error StdDev Median Gen 0 Allocated Immutype record struct 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - - With record struct 0.0317 ns 0.0287 ns 0.0307 ns 0.0240 ns - - With record 6.1894 ns 0.1489 ns 0.2797 ns 6.3389 ns 0.0029 24 B Immutype record 7.2829 ns 0.1577 ns 0.1619 ns 7.2482 ns 0.0029 24 B С двумя свойствами string и int: Method Mean Error StdDev Median Gen 0 Allocated With record struct 0.0195 ns 0.0296 ns 0.0291 ns 0.0036 ns - - Immutype record struct 0.0442 ns 0.0257 ns 0.0352 ns 0.0285 ns - - With record 8.2670 ns 0.1532 ns 0.1280 ns 8.2548 ns 0.0038 32 B Immutype record 10.6809 ns 0.2420 ns 0.4424 ns 10.8150 ns 0.0076 64 B С четырьмя свойствами string, int, string и bool: Method Mean Error StdDev Median Gen 0 Allocated Immutype record struct 0.0409 ns 0.0074 ns 0.0058 ns 0.0407 ns - - With record struct 0.0695 ns 0.0275 ns 0.0633 ns 0.0491 ns - - With record 10.0555 ns 0.2268 ns 0.3790 ns 9.8518 ns 0.0048 40 B Immutype record 25.2299 ns 0.5267 ns 0.8200 ns 24.9839 ns 0.0191 160 B Legends: Mean - Arithmetic mean of all measurements Error - Half of 99.9% confidence interval StdDev - Standard deviation of all measurements Median - Value separating the higher half of all measurements (50th percentile) Gen 0 - GC Generation 0 collects per 1000 operations Allocated - Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) 1 ns - 1 Nanosecond (0.000000001 sec) Производительность Immutype для class и struct будет близкой к производительности для record и record struct. Важно отметить, что значимые траты производительности при использовании статических методов Immutype для ссылочных типов - это проверка входных параметров на null. Программист