Immutable types in C# with Roslyn

栏目: IT技术 · 发布时间: 5年前

内容简介:Some time ago I came across Jimmy Bogard’s articleAs I’ve mentioned before, in order to make a class immutable we need to remove setters, add a dedicated constructor for initializing those properties and then always use this constructor to instantiate our
Immutable types in C# with Roslyn

Some time ago I came across Jimmy Bogard’s article “Immutability in DTOs?” about the pros and cons of using immutable type pattern/approach. I fully agree with the author - the idea of immutable types is great but without the proper support from the language syntax it might not be worth applying. C# allows creating immutable types by adding readonly keyword to fields or by removing setter from properties. We are obligated then to initialize those readonly members from the constructor or directly in the member’s definition. This results in a large amount of boilerplate code, causes problems with ORMs and serializers which require a default constructor, and makes the object instantiation cumbersome (or at least less readable). I’m a huge fan of Roslyn so I’ve started thinking about how to utilize Roslyn’s API to avoid all those problems with immutable types and improve coding experience while working with them. In this article, I’m going to present the results of my experiments with Roslyn analyzers that simulate types immutability.

Convenient initialization

As I’ve mentioned before, in order to make a class immutable we need to remove setters, add a dedicated constructor for initializing those properties and then always use this constructor to instantiate our immutable class. A sample code snippet just to remind you how cumbersome it is:

public class UserDTO
{
    public string FirstName { get;}
    public string LastName { get;}
    public int Age { get; }

    public UserDTO(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var user = new UserDTO("John", "Doe", 20);
    }
}

It would be more convenient if we didn’t need to define a constructor and initialize members using the initialization block. However, there’s no mechanism that allows enforcing mandatory initialization in the init block. So, let’s introduce [InitRequired] attribute inspired by initonly keyword from C# records proposal :

Immutable types in C# with Roslyn

If we want to enforce mandatory initialization via initialization block for all members, we can mark our type with [InitRequired] attribute.

Immutable types in C# with Roslyn

Of course, the property to be able to initialize via init block it must meet certain conditions:

  • must have a setter
  • the setter needs to be available in a given context (accessibility)
  • cannot be a part of explicit interface implementation.

In order to avoid missing initialization caused by the conditions mentioned above, I would recommend always keeping those properties on the same accessibility level as the containing type.

Pro Tip:You can use MappingGenerator to complete initialization block with local accessible values Immutable types in C# with Roslyn or to scaffold this initialization with sample values Immutable types in C# with Roslyn

If you don’t have access to the source code or you want to enforce full initialization only for given instance, you can do that by adding /*FullInitRequired*/ comment marker:

Immutable types in C# with Roslyn

To ensure that full object graph is initialized, use /*FullInitRequired:recursive*/ comment marker. I think this may be especially useful for methods performing mapping or deep clone. I got the idea of those comment markers from a discussion about String Hints #2796 . This kind of annotation is already used for marking string literals with regex pattern:

Immutable types in C# with Roslyn

[InitRequired] attribute enforces only mandatory initialization via initialization block. To achieve immutability, we need to forbid modification outside the init block. For that purpose, I’ve introduced [InitOnly] attribute. Basically, it works in the same way as [InitRequired] but additionally it verifies if members decorated with it are not modified after initialization.

Immutable types in C# with Roslyn

You can enforce immutability for all members by putting [InitOnly] attribute on the type level.

Immutable types in C# with Roslyn

Thanks to [InitOnly] attribute and corresponding analyzer we can achieve full immutability without writing redundant boilerplate code.

Important:If you like the idea of [InitRequire] and [InitOnly] attributes and you are going to use them in your project, please make sure that all your teammates know about it.

All attributes and analyzers described here are available as a single Nuget package SmartAnalyzers.CSharpExtensions.Annotations . The source code is published on Github under CSharpExtensions project. Please let me know what you think about those extensions to C# language and if you encounter any problems with using it, feel free to report an issue on Github page.

If you find this blog post useful and you think it's worth to share this idea with others, please don't hesitate to use these buttons below:


以上所述就是小编给大家介绍的《Immutable types in C# with Roslyn》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

腾讯方法

腾讯方法

潘东燕、王晓明 / 机械工业出版社 / 2014-12-11 / 39.00

这是国内第一本深度讲述腾讯产品研发与团队转型的书。本书介绍了腾讯三个不同生命周期的产品的开发过程,包括如何踏足新领域开发新产品;如何救活一个即将半路夭折的产品;如何让一个老产品持续盈利。本书呈现了互联网产品开发时会遇到普遍问题和解决方法,涉及大企业如何内部创业,并迅速组建新的项目团队;如何实现跨部门的合作;在面临新团队和紧急开发任务时如何提高团队沟通效率;在产品研发方面,如何定位产品、如何敏捷开发......一起来看看 《腾讯方法》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

html转js在线工具
html转js在线工具

html转js在线工具