{"id":407893,"date":"2023-11-23T16:06:31","date_gmt":"2023-11-23T15:06:31","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=dotnet&#038;p=407893"},"modified":"2024-03-26T15:49:19","modified_gmt":"2024-03-26T14:49:19","slug":"primary-constructors-using-csharp-12-in-rider-and-resharper","status":"publish","type":"dotnet","link":"https:\/\/blog.jetbrains.com\/zh-hans\/dotnet\/2023\/11\/23\/primary-constructors-using-csharp-12-in-rider-and-resharper","title":{"rendered":"Primary Constructors \u2013 Using C# 12 in Rider and ReSharper"},"content":{"rendered":"\n<figure class=\"alignright is-resized\" style=\"margin-top: 0;\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2017\/09\/dotnet-csharp.png\" alt=\"ReSharper and Rider support for C# 12\" class=\"wp-image-16074\" width=\"200\" height=\"200\" style=\"margin: 10px\"><\/figure>\n\n\n\n<p>Welcome to our series, where we take a <strong>closer look at the <a href=\"https:\/\/github.com\/dotnet\/roslyn\/blob\/main\/docs\/Language%20Feature%20Status.md#c-120\" target=\"_blank\" rel=\"noopener\">C# 12 language features<\/a><\/strong> and how ReSharper and Rider make it easy for you to adopt them in your codebase. If you haven\u2019t yet, <a href=\"https:\/\/dotnet.microsoft.com\/en-us\/download\/dotnet\" target=\"_blank\" rel=\"noopener\">download the latest .NET 8 SDK<\/a> and <a href=\"https:\/\/blog.jetbrains.com\/zh-hans\/dotnet\/2021\/10\/28\/file-scoped-namespaces-in-csharp-10#how-to-start-using-c-10\">update your project files<\/a>!<\/p>\n\n\n\n<p>In this series, we are looking at:<\/p>\n\n\n\n<ul>\n<li><strong>Primary Constructors<\/strong><\/li>\n\n\n\n<li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2024\/01\/16\/interceptors-using-csharp-12-in-rider-and-resharper\/\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/dotnet\/2024\/01\/16\/interceptors-using-csharp-12-in-rider-and-resharper\/\">Interceptors<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2023\/09\/25\/rsrp-2023-3-eap1\/\">Alias Any Type<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2024\/03\/26\/collection-expressions-using-csharp-12-in-rider-and-resharper\/\">Collection Expressions<\/a><\/li>\n<\/ul>\n\n\n\n<p>This is the first post, so let&#8217;s dive into primary constructors.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Background &amp; Syntax<\/h2>\n\n\n\n<p>If we look at our codebases, we will realize that many of our classes and structs only define a single constructor to receive inputs and perform trivial initializations or pass values to the base constructor. Declaring these constructors involves tedious and boilerplate syntax, like access modifiers, repeated type names, and more braces. In C# 9, we saw a terse syntax for primary constructors in <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-9.0\/records\" target=\"_blank\" rel=\"noopener\">record declarations<\/a>. With the release of C# 12, we finally get <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-12.0\/primary-constructors\" target=\"_blank\" rel=\"noopener\">primary constructors for regular classes and structs<\/a> (non-records) in our developer toolbox.<\/p>\n\n\n\n<p>Unlike primary constructors for records \u2013 which implicitly declare properties corresponding to constructor parameters \u2013 primary constructors in regular classes\/structs will not automatically expose their parameters. They also won&#8217;t get auto-generated members for <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-9.0\/records#equality-members\" target=\"_blank\" rel=\"noopener\">equality<\/a>, <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-9.0\/records#deconstruct\" target=\"_blank\" rel=\"noopener\">deconstruction<\/a>, and <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-9.0\/records#copy-and-clone-members\" target=\"_blank\" rel=\"noopener\">cloning<\/a>. It\u2019s more common for regular classes\/structs to encapsulate data into private fields instead of exposing it publicly.<\/p>\n\n\n\n<p>Here&#8217;s a <a href=\"https:\/\/sharplab.io\/#v2:EYLgtghglgdgPgAQMwAIECYUBECmAnKANxwBMAKBARgAYUBnAGhVgBdmngB7TgGxWACUKECgBCEOjjJ0BAWABQAbwUpVaVKxQBJFIpQBzHCwDc9I6YC+KALzNjKtcjQ0xNgHz8AdABVOAZRYCGH0yAXt5CwUFJwwxCSkqWhlwoA=\" target=\"_blank\" rel=\"noopener\">simple example<\/a> of a class using primary constructors:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public class Derived(int i, string s, bool b) : Base(s)\n{\n    public int I { get; set; } = i;\n    public string B => b.ToString();\n}\n\npublic class Base(string s); \/\/ no body required\n<\/pre>\n\n\n\n<p>Now let&#8217;s unfold what happens to our class parameters:<\/p>\n\n\n\n<ul>\n<li><code>string s<\/code> is passed on to our base type<\/li>\n\n\n\n<li><code>int i<\/code> is used to initialize an <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/classes-and-structs\/auto-implemented-properties\" target=\"_blank\" rel=\"noopener\">auto-property<\/a><\/li>\n\n\n\n<li><code>bool b<\/code> is the first parameter where the magic happens\u2026<\/li>\n<\/ul>\n\n\n\n<p>Primary constructor parameters are scoped to the whole class. That means any instance member \u2013 except secondary constructors \u2013 can access them. The C# compiler makes this happen by inferring the set of private fields required for us and capturing the corresponding value in those fields. It&#8217;s worth noting that a capture only occurs if the parameter is accessed from an instance member.<\/p>\n\n\n\n<p>In low-level C#, our example compiles as follows:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public class Derived : Base\n{\n    [CompilerGenerated] private bool &lt;b>P;\n\n    [CompilerGenerated] private int &lt;I>k__BackingField;\n\n    public int I\n    {\n        [CompilerGenerated] get => &lt;I>k__BackingField;\n        [CompilerGenerated] set => &lt;I>k__BackingField = value;\n    }\n\n    public string B => &lt;b>P.ToString();\n\n    public Derived(string s, int i, bool b)\n    {\n        &lt;b>P = b;\n        &lt;I>k__BackingField = i;\n        base..ctor(s);\n    }\n}\n<\/pre>\n\n\n\n<p><em>Try <a href=\"https:\/\/www.jetbrains.com\/help\/rider\/Viewing_Intermediate_Language.html\" target=\"_blank\" rel=\"noopener\">Rider&#8217;s IL Viewer<\/a> to see the magic happening in your own code<\/em>!<\/p>\n\n\n\n<p>Now that we&#8217;ve dealt with the basics, let&#8217;s see how ReSharper and Rider help you work with primary constructors! We will focus primarily (pun intended) on new features and less on existing features that have been updated for primary constructors (including syntax parsing, refactorings, etc.).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conversion &amp; Simplification<\/h2>\n\n\n\n<p>As with every new C# syntax feature, .NET developers face the <strong>challenge to adapt<\/strong>. Not only would we like to know if something can be written more elegantly, but also we might like to convert all our existing code to the new and shiny. Well, ReSharper and Rider definitely got you covered there!<\/p>\n\n\n\n<p>If we take our example from above written with an explicit constructor, you will see a suggestion to <strong>convert to primary constructor<\/strong>. Did we rewrite that example manually? No, there\u2019s also a context action to <strong>convert to explicit constructor<\/strong>!<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/convert-primary-constructor.jpg\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/convert-primary-constructor.png\" alt=\"Converting between primary and explicit constructors\" width=\"600\"\/><figcaption class=\"wp-element-caption\">Converting between primary and explicit constructors<\/figcaption><\/figure>\n\n\n\n<p>If you fall for an old habit and assign parameters to fields, ReSharper and Rider will help you to <strong>replace field usages with primary constructor parameters<\/strong> (or vice versa if you actually want it):<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/convert-field.jpg\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/convert-field.gif\" alt=\"Replacing field usages with primary constructor parameters\" width=\"620\"\/><figcaption class=\"wp-element-caption\">Replacing field usages with primary constructor parameters<\/figcaption><\/figure>\n\n\n\n<p>As with plenty of our inspections, remember that there\u2019s a way to <strong>apply them in bulk<\/strong> on any scope you like. If you want to simplify constructors in your whole solution, you can do this effortlessly:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1568\" height=\"614\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/convert-in-solution.png\" alt=\"Converting to primary constructors in the whole solution\" class=\"wp-image-407916\"\/><figcaption class=\"wp-element-caption\">Converting to primary constructors in the whole solution<\/figcaption><\/figure><\/div>\n\n\n<p>Closing the chapter on simplifications for primary constructors, you can also <strong>remove redundant constructors and bodies<\/strong> with a quick-fix easily:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/redundant-body.jpg\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/redundant-body.gif\" alt=\"Removing empty redundant primary constructors and bodies\" width=\"450\"\/><figcaption class=\"wp-element-caption\">Removing empty redundant primary constructors and bodies<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Double Capture Warnings<\/h2>\n\n\n\n<p>We already talked about capturing, but what exactly is <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-12.0\/primary-constructors#double-storage-warnings\" target=\"_blank\" rel=\"noopener\">double capturing<\/a>, and why is it bad? Let&#8217;s consider the following example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">public class Person(int age)\n{\n    \/\/ initialization\n    public int Age { get; set; } = age;\n\n    \/\/ capture\n    public string Bio => $\"My age is {age}!\";\n}<\/pre>\n\n\n\n<p>In this class, the parameter <code>age<\/code> is exposed both through the <code>Age<\/code> and <code>Bio<\/code> property. As a result, the object stores the state of <code>age<\/code> twice! For reference types, a double capture leads to an increased memory footprint and possibly even memory leaks. In our concrete example, you will observe the following unintended behavior:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var p = new Person(42);\np.Age.Dump();   \/\/ Output: 42\np.Bio.Dump();   \/\/ Output: My age is 42!\n\np.Age++;\np.Age.Dump();   \/\/ Output: 43\np.Bio.Dump();   \/\/ Output: My age is 42! \/\/ !!!!\n<\/pre>\n\n\n\n<p>ReSharper and Rider notify you about potential double-capture issues through an inspection:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1688\" height=\"438\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/double-capture-warning2.png\" alt=\"\" class=\"wp-image-408597\"\/><figcaption class=\"wp-element-caption\">Double capturing inspection<\/figcaption><\/figure>\n\n\n\n<p>And, of course, there is also a quick-fix to <strong>replace captures with property access<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/replace-capture-with-property2.jpg\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/replace-capture-with-property2.gif\" alt=\"Fixing double capture with property usage\" width=\"480\"\/><figcaption class=\"wp-element-caption\">Fixing double capture with property usage<\/figcaption><\/figure>\n\n\n\n<p>Unnecessary captures can also occur in more involved situations, for example, in type hierarchies where the derived type captures it while it could use a property from the base type. In this case, the quick-fix will suggest to <strong>use the initialized property from the base type<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/replace-capture-with-base-property.jpg\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/replace-capture-with-base-property.gif\" alt=\"Fixing double capture with base-type property usage\" width=\"700\"\/><figcaption class=\"wp-element-caption\">Fixing double capture with base-type property usage<\/figcaption><\/figure>\n\n\n\n<p>Of course, there might be no property in the base type just yet. ReSharper and Rider will still let you know so that you can take action and extract the captured parameter into a property.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Semantic Highlighting<\/h2>\n\n\n\n<p>Personally, I&#8217;m a big fan of how ReSharper and Rider augment your code with more information through visual cues like colors and inlay hints. For primary constructors, in particular, we thought it would be great to <strong>see<\/strong> <strong>whether a parameter is captured at a glance<\/strong>. In our initial example, you will see that <code>b<\/code> is slightly blue:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/color-scheme.png\" alt=\"Highlighting for captured primary constructor parameters\" width=\"580\"\/><figcaption class=\"wp-element-caption\">Highlighting for captured primary constructor parameters<\/figcaption><\/figure>\n\n\n\n<p>If the difference is too subtle for you, you can tweak your color scheme under:<\/p>\n\n\n\n<ul>\n<li>In Rider, under <em><strong>Settings | Editor | Color Scheme | C# | Properties and variables | Primary Constructor Parameter<\/strong><\/em><\/li>\n\n\n\n<li>In ReSharper, under <em><strong>Options | Environment | Fonts and Colors | Display items | ReSharper C# Primary Constructor Parameter<\/strong><\/em><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Code Style &amp; Formatting<\/h2>\n\n\n\n<p>Every team wants their code to be styled and formatted a bit differently. ReSharper and Rider help you solve this task automatically through the <strong><a href=\"https:\/\/www.jetbrains.com\/help\/rider\/Enforcing_Code_Formatting_Rules.html\" target=\"_blank\" rel=\"noopener\">Reformat Code<\/a><\/strong> action! As part of our primary constructor support, we\u2019ve added a couple new code style settings under <em><strong>Tabs, Indents, Alignment<\/strong>,<\/em> and <em><strong>Line Breaks and Wrapping<\/strong><\/em> specifically for primary constructors:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/code-style-tabs-indents-align.png\" alt=\"Tabs, Indents, Alignment settings for primary constructors\" width=\"700\"\/><figcaption class=\"wp-element-caption\">Tabs, Indents, Alignment settings for primary constructors<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full aligncenter\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/11\/code-style-line-breaks-wrapping.png\" alt=\"Line Breaks and Wrapping settings for primary constructors\" width=\"700\"\/><figcaption class=\"wp-element-caption\">Line Breaks and Wrapping settings for primary constructors<\/figcaption><\/figure>\n\n\n\n<p>You can try these new settings by selecting a code block and invoking the <strong><a href=\"https:\/\/www.jetbrains.com\/help\/rider\/Configure_Code_Formatting_Rules.html#configure-formatting-rules-for-selected-code\" data-type=\"link\" data-id=\"https:\/\/www.jetbrains.com\/help\/rider\/Configure_Code_Formatting_Rules.html#configure-formatting-rules-for-selected-code\" target=\"_blank\" rel=\"noopener\">Configure code style<\/a><\/strong> action. Alternatively, you can also grab the following snippet and adapt the formatter comments:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ @formatter:wrap_primary_constructor_parameters_style chop_always \/\/ wrap_if_long | chop_always\n\/\/ @formatter:max_primary_constructor_parameters_on_line 3\n\/\/ @formatter:indent_primary_constructor_decl_pars inside \/\/ inside | outside_and_inside | outside | none\n\/\/ @formatter:keep_existing_primary_constructor_declaration_parens_arrangement true\n\/\/ @formatter:wrap_before_primary_constructor_declaration_rpar false\n\/\/ @formatter:wrap_before_primary_constructor_declaration_lpar false\n\nfile class Formatting(int number, string text, bool boolean);\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>In this post, we discovered a lot of features related to the new C# 12 primary constructors. <strong>Try <a href=\"https:\/\/www.jetbrains.com\/resharper\/nextversion\/\" target=\"_blank\" rel=\"noopener\">ReSharper 2023.3 EAP<\/a> or <a href=\"https:\/\/www.jetbrains.com\/rider\/nextversion\/\" target=\"_blank\" rel=\"noopener\">Rider 2023.3 EAP<\/a><\/strong> now \u2013 not only to improve your productivity but also to save yourself from nasty bugs. If you see any opportunities for additional support, please let us know in the comments below! As always, thank you for reading.<\/p>\n\n\n\n<p><em><sub>Image credit: <a href=\"https:\/\/unsplash.com\/photos\/black-blue-red-and-yellow-round-illustration-bHPI-o3C1aQ\" data-type=\"link\" data-id=\"https:\/\/unsplash.com\/photos\/black-blue-red-and-yellow-round-illustration-bHPI-o3C1aQ\" target=\"_blank\" rel=\"noopener\">Dollar Gill<\/a><\/sub><\/em><\/p>\n","protected":false},"author":553,"featured_media":408054,"comment_status":"closed","ping_status":"closed","template":"","categories":[4992,1401],"tags":[8319,600,756,3912,659,46,1978],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/dotnet\/407893"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/dotnet"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/dotnet"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/553"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=407893"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/dotnet\/407893\/revisions"}],"predecessor-version":[{"id":460546,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/dotnet\/407893\/revisions\/460546"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media\/408054"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=407893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=407893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=407893"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=407893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}