{"id":51534,"date":"2024-04-23T10:00:00","date_gmt":"2024-04-23T17:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=51534"},"modified":"2024-04-23T08:47:50","modified_gmt":"2024-04-23T15:47:50","slug":"csharp-primary-constructors-refactoring","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/csharp-primary-constructors-refactoring\/","title":{"rendered":"Refactor your C# code with primary constructors"},"content":{"rendered":"<p><a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12\">C# 12 as part of .NET 8<\/a> introduced a compelling set of new features! In this post, we explore one of these features, specifically <em>primary constructors<\/em>, explaining its usage and relevance. We&#8217;ll then demonstrate a sample refactoring to show how it can be applied in your code, discussing the benefits and potential pitfalls. This will help you understand the impact of the change and help influence your adoption of the feature.<\/p>\n<h2>Primary Constructors 1\ufe0f\u20e3<\/h2>\n<p>Primary constructors are considered an &#8220;Everyday C#&#8221; developer feature. They allow you to define a <code>class<\/code> or <code>struct<\/code> along with its constructor in a single concise declaration. This can help you reduce the amount of boilerplate code you need to write. If you&#8217;ve been following along with C# versions, you&#8217;re likely familiar with <code>record<\/code> types, which included the first examples of primary constructors.<\/p>\n<h3>Differentiating from <code>record<\/code> types<\/h3>\n<p><a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/fundamentals\/types\/records\">Record types<\/a> were introduced as a type modifier of <code>class<\/code> or <code>struct<\/code> that simplifies syntax for building simple classes like data containers. Records can include a primary constructor. This constructor not only generates a backing field but also exposes a public property for each parameter. Unlike traditional <code>class<\/code> or <code>struct<\/code> types, where primary constructor parameters are accessible throughout the class definition, records are designed to be transparent data containers. They inherently support value-based equality, aligning with their intended role as data holders. Consequently, it&#8217;s logical for their primary constructor parameters to be accessible as properties.<\/p>\n<h3>Refactoring example\u2728<\/h3>\n<p><a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/tools\/dotnet-new\">.NET provides many templates<\/a>, and if you&#8217;ve ever created a <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/extensions\/workers\">Worker Service<\/a>, you&#8217;ve likely seen the following <code>Worker<\/code> class template code:<\/p>\n<pre><code class=\"language-csharp\">namespace Example.Worker.Service\r\n{\r\n    public class Worker : BackgroundService\r\n    {\r\n        private readonly ILogger&lt;Worker&gt; _logger;\r\n\r\n        public Worker(ILogger&lt;Worker&gt; logger)\r\n        {\r\n            _logger = logger;\r\n        }\r\n\r\n        protected override async Task ExecuteAsync(CancellationToken stoppingToken)\r\n        {\r\n            while (!stoppingToken.IsCancellationRequested)\r\n            {\r\n                if (_logger.IsEnabled(LogLevel.Information))\r\n                {\r\n                    _logger.LogInformation(\"Worker running at: {time}\", DateTimeOffset.Now);\r\n                }\r\n                await Task.Delay(1000, stoppingToken);\r\n            }\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>The preceding code is a simple <code>Worker<\/code> service that logs a message every second. Currently, the <code>Worker<\/code> class has a constructor that requires an <code>ILogger&lt;Worker&gt;<\/code> instance as a parameter and assigns it to a <code>readonly<\/code> field of the same type. This type information is in two places, in the definition of the constructor, but also on the field itself. This is a common pattern in C# code, but it can be simplified with primary constructors.<\/p>\n<p>It&#8217;s worth mentioning that the refactoring tooling for this specific feature isn&#8217;t available in Visual Studio Code, but you can still refactor to primary constructors manually. To refactor this code using primary constructors in Visual Studio, you can use the <code>Use primary constructor (and remove fields)<\/code> refactoring option. Right-click on the <code>Worker<\/code> constructor, select <code>Quick Actions and Refactorings...<\/code> (or press <kbd>Ctrl<\/kbd> + <kbd>.<\/kbd>), and choose <code>Use primary constructor (and remove fields)<\/code>.<\/p>\n<p>Consider the following video demonstrating <em>Use primary constructor<\/em> refactoring functionality:<\/p>\n<p><div style=\"width: 1914px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-51534-1\" width=\"1914\" height=\"1032\" poster=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-primary-ctor-thumb.png\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-primary-ctor.mp4?_=1\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-primary-ctor.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-primary-ctor.mp4<\/a><\/video><\/div><\/p>\n<p>The resulting code now resembles the following C# code:<\/p>\n<pre><code class=\"language-csharp\">namespace Example.Worker.Service\r\n{\r\n    public class Worker(ILogger&lt;Worker&gt; logger) : BackgroundService\r\n    {\r\n        protected override async Task ExecuteAsync(CancellationToken stoppingToken)\r\n        {\r\n            while (!stoppingToken.IsCancellationRequested)\r\n            {\r\n                if (logger.IsEnabled(LogLevel.Information))\r\n                {\r\n                    logger.LogInformation(\"Worker running at: {time}\", DateTimeOffset.Now);\r\n                }\r\n                await Task.Delay(1000, stoppingToken);\r\n            }\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>That&#8217;s it, you&#8217;ve successfully refactored the <code>Worker<\/code> class to use a primary constructor! The <code>ILogger&lt;Worker&gt;<\/code> field has been removed, and the constructor has been replaced with a primary constructor. This makes the code more concise and easier to read. The <code>logger<\/code> instance is now available throughout the class (as it&#8217;s in scope), without the need for a separate field declaration.<\/p>\n<h2>Additional considerations \ud83e\udd14<\/h2>\n<p>Primary constructors can remove your hand-written field declarations that were assigned in the constructor, but with a caveat. They&#8217;re not entirely functionally equivalent if you have defined your fields as <code>readonly<\/code> because primary constructor parameters for non-record types are mutable. So, when you&#8217;re using this refactoring approach, be aware that you&#8217;re changing the semantics of your code. If you want to maintain the <code>readonly<\/code> behavior, use a field declaration in place and assign the field using the primary constructor parameter:<\/p>\n<pre><code class=\"language-csharp\">namespace Example.Worker.Service;\r\n\r\npublic class Worker(ILogger&lt;Worker&gt; logger) : BackgroundService\r\n{\r\n    private readonly ILogger&lt;Worker&gt; _logger = logger;\r\n\r\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\r\n    {\r\n        while (!stoppingToken.IsCancellationRequested)\r\n        {\r\n            if (_logger.IsEnabled(LogLevel.Information))\r\n            {\r\n                _logger.LogInformation(\"Worker running at: {time}\", DateTimeOffset.Now);\r\n            }\r\n            await Task.Delay(1000, stoppingToken);\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h2>Additional constructors \ud83c\udd95<\/h2>\n<p>When you define a primary constructor, you can still define additional constructors. These constructors are required, however; to call the primary constructor. Calling the primary constructor ensures that the primary constructor parameters are initialized everywhere in the class declaration. If you need to define additional constructors, you must call the primary constructor using the <code>this<\/code> keyword.<\/p>\n<pre><code class=\"language-csharp\">namespace Example.Worker.Service\r\n{\r\n    \/\/ Primary constructor\r\n    public class Worker(ILogger&lt;Worker&gt; logger) : BackgroundService\r\n    {\r\n        private readonly int _delayDuration = 1_000;\r\n\r\n        \/\/ Secondary constructor, calling the primary constructor\r\n        public Worker(ILogger&lt;Worker&gt; logger, int delayDuration) : this(logger)\r\n        {\r\n            _delayDuration = delayDuration;\r\n        }\r\n\r\n        \/\/ Omitted for brevity...\r\n    }\r\n}<\/code><\/pre>\n<p>Additional constructors aren&#8217;t always needed. Let&#8217;s do some bonus refactoring to include a few other features!<\/p>\n<h2>Bonus refactoring \ud83c\udf89<\/h2>\n<p>Primary constructors are awesome, but there&#8217;s more we can do to improve the code.<\/p>\n<p>C# includes <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/language-reference\/keywords\/namespace\">file-scoped namespaces<\/a>. They&#8217;re a really nice feature that reduces a level of nesting and improves readability. Continuing with the previous example, place your cursor at the end of the namespace name, and press the <kbd>;<\/kbd> key (this isn&#8217;t supported in Visual Studio Code, but again you can do this manually). This will convert the namespace to a file-scoped namespace.<\/p>\n<p>Consider the following video demonstrating this functionality:<\/p>\n<p><div style=\"width: 1914px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-51534-2\" width=\"1914\" height=\"1032\" poster=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-file-scope-ns-thumb.png\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-file-scope-ns.mp4?_=2\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-file-scope-ns.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2024\/04\/refactor-file-scope-ns.mp4<\/a><\/video><\/div><\/p>\n<p>With a few additional edits, the final refactored code is as follows:<\/p>\n<pre><code class=\"language-csharp\">namespace Example.Worker.Service;\r\n\r\npublic sealed class Worker(ILogger&lt;Worker&gt; logger) : BackgroundService\r\n{\r\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\r\n    {\r\n        while (!stoppingToken.IsCancellationRequested)\r\n        {\r\n            if (logger.IsEnabled(LogLevel.Information))\r\n            {\r\n                logger.LogInformation(\"Worker running at: {time}\", DateTimeOffset.Now);\r\n            }\r\n\r\n            await Task.Delay(1_000, stoppingToken);\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<p>In addition to refactoring to file-scoped namespaces, I also added the <code>sealed<\/code> modifier, as there&#8217;s a performance benefit in multiple situations. Finally, I&#8217;ve also updated the numeric literal passed into the <code>Task.Delay<\/code> using the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/language-reference\/builtin-types\/integral-numeric-types#integer-literals\">digit separator<\/a> feature, to improve the readability. Did you know there&#8217;s a lot more to simplify your code? Check out <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\">What&#8217;s new in C#<\/a> to learn more!<\/p>\n<h2>Next steps \ud83d\ude80<\/h2>\n<p>Try this out in your own code! Look for opportunities to refactor your code to use primary constructors and see how it can simplify your codebase. If you&#8217;re using Visual Studio, check out the refactoring tooling. If you&#8217;re using Visual Studio Code, you can still refactor manually. To learn more, explore the following resources:<\/p>\n<ul>\n<li><a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/tutorials\/primary-constructors\">Primary constructors in C# 12<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/visualstudio\/ide\/quick-actions?view=vs-2022\">Visual Studio: Additional Quick Actions<\/a><\/li>\n<li><a href=\"https:\/\/code.visualstudio.com\/docs\/csharp\/refactoring\">Visual Studio Code: C# Quick Actions and Refactorings<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Explore C# 12&#8217;s primary constructors through incremental refactoring of a Worker service.<\/p>\n","protected":false},"author":24662,"featured_media":51535,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756],"tags":[7701,7727],"class_list":["post-51534","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","tag-dotnet-8","tag-c-12"],"acf":[],"blog_post_summary":"<p>Explore C# 12&#8217;s primary constructors through incremental refactoring of a Worker service.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/51534","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/24662"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=51534"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/51534\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/51535"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=51534"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=51534"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=51534"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}