{"id":326405,"date":"2023-03-14T17:14:56","date_gmt":"2023-03-14T16:14:56","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=dotnet&#038;p=326405"},"modified":"2023-03-14T17:36:43","modified_gmt":"2023-03-14T16:36:43","slug":"static-interface-members-generic-attributes-auto-default-structs-using-csharp-11-in-rider-and-resharper","status":"publish","type":"dotnet","link":"https:\/\/blog.jetbrains.com\/en\/dotnet\/2023\/03\/14\/static-interface-members-generic-attributes-auto-default-structs-using-csharp-11-in-rider-and-resharper","title":{"rendered":"Static Interface Members, Generic Attributes, Auto-Default Structs \u2013 Using C# 11 in Rider and ReSharper"},"content":{"rendered":"\n<figure class=\"alignright is-resized\"><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# 11\" class=\"wp-image-16074\" width=\"200\" height=\"200\" style=\"margin: 10px\"><\/figure>\n\n\n\n<p>The .NET 7 SDK arrived a few months ago, with many .NET developers looking forward to this release and the brand-new C# language features that come along with it. If you haven&#8217;t put your fingers on it yet, all you need is:<\/p>\n\n\n\n<ul><li>Download the <a href=\"https:\/\/dotnet.microsoft.com\/en-us\/download\/dotnet\/7.0\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">latest .NET SDK<\/a><\/li><li>Update your <code>global.json<\/code> if you have one<\/li><li>Update the <code>TargetFramework<\/code> in your project to <code>net7.0<\/code> (only for some features)<\/li><li>Update the <code>LangVersion<\/code> property in your project to <code>11.0<\/code> or <code>preview<\/code><\/li><\/ul>\n\n\n\n<p>In this series, we will dive into the most interesting features that are coming with C# 11 and show how we updated ReSharper and Rider to support you in applying them to your codebase with ease:<\/p>\n\n\n\n<ul><li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2023\/02\/20\/list-and-span-pattern-matching-using-csharp-11-in-rider-and-resharper\/\">List and Span Pattern Matching<\/a><\/li><li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2023\/02\/27\/raw-strings-utf-8-strings-multiline-interpolations-using-csharp-11-in-rider-and-resharper\/\">Raw Strings, UTF-8 Strings, and Multiline Interpolations<\/a><\/li><li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2023\/03\/06\/required-keyword-checked-operators-nameof-operator-scope-using-csharp-11-in-rider-and-resharper\/\">Required Keyword, Checked Operators, nameof Operator Scope<\/a><\/li><li><a href=\"https:\/\/blog.jetbrains.com\/dotnet\/2023\/03\/14\/static-interface-members-generic-attributes-auto-default-structs-using-csharp-11-in-rider-and-resharper\/\"><strong>Static Interface Members, Generic Attributes, Auto-Default Structs<\/strong><\/a><\/li><\/ul>\n\n\n\n<p>This fourth post will walk you through the static interface members, generic attributes, and auto-default structs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Static Interface Members<\/h2>\n\n\n\n<p>Once <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/classes-and-structs\/extension-methods\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">extension methods<\/a> were brought in with C# 3, developers could easily extend existing interfaces and their inheritors to provide additional functionality without introducing breaking changes (i.e., without requiring inheritors to implement new members):<\/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=\"\">interface IMyInterface { }\n\nstatic class Extensions\n{\n    public static void Method(this IMyInterface obj) { \/* ... *\/ }\n}<\/pre>\n\n\n\n<p>With C# 8, the language team added <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/language-reference\/proposals\/csharp-8.0\/default-interface-methods\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">default interface members<\/a>, and the approach to extending an interface slightly improved. Now you could add a static constructor for the interface, define real properties (as opposed to <code>Get&lt;Property&gt;<\/code> methods), and allow inheritors to override the default implementation with something more specific. Again \u2013 without making breaking changes:<\/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=\"\">interface IMyInterface\n{\n    static IMyInterface() { \/* ... *\/ }\n\n    public string Property => \"default\";\n}\n\nclass Implementation : IMyInterface\n{\n    public string Property => \"overridden\";\n}<\/pre>\n\n\n\n<p><strong>In C# 11, the introduction of <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/whats-new\/tutorials\/static-virtual-interface-members\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">static virtual members<\/a> once again lifts the restrictions<\/strong>. While extension methods and the first wave of default interface members only allowed adding <em>instance-like<\/em> members (working in static context though), this latest improvement allows you to add static members as well. Those members can be declared with or without a default implementation:<\/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=\"\">interface IMyInterface\n{\n    static abstract string Foo { get; }    \/\/ without implementation\n    static virtual string Bar => \"value\";  \/\/ with implementation\n}<\/pre>\n\n\n\n<p>The definition of abstract members in interfaces was particularly motivated by the <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/standard\/generics\/math\" target=\"_blank\" rel=\"noreferrer noopener nofollow\" title=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/standard\/generics\/math\">generic math<\/a> feature, which works on the idea of defining constants and operators for numeric types inheriting from <code>INumber&lt;T&gt;<\/code> or similar abstractions, thus allowing them to be used with general mathematical algorithms:<\/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 interface INumber&lt;TSelf> : ... { }\n\npublic interface IAdditionOperators&lt;TSelf, TOther, TResult>\n{\n    static abstract TResult operator +(TSelf left, TOther right);\n}\n\npublic interface IAdditiveIdentity&lt;TSelf, TResult>\n{\n    static abstract TResult AdditiveIdentity { get; }\n}<\/pre>\n\n\n\n<p>Static interface members can also be used to reduce allocations <a href=\"https:\/\/youtu.be\/KKo0qJUlXGI\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">as Nick Chapses shows<\/a> in one of his videos. Instead of having to <code>new T()<\/code> an interface implementation, you can call the static member directly:<\/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=\"\">void AccessPropertyThroughStatic&lt;T>() where T : IWithStaticVirtual\n    => Console.WriteLine(T.Text);\n\nvoid AccessPropertyThroughInstance&lt;T>() where T : IWithoutStaticAbstract, new()\n    => Console.WriteLine(new T().Text);<\/pre>\n\n\n\n<p><strong>ReSharper and Rider make it easy to work with static interface members<\/strong>. If a type does not implement a <code>static abstract<\/code> member yet, you&#8217;ll see the usual error with a quick-fix to create the declaration:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/03\/csharp11-interface-static-member.png\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/03\/csharp11-interface-static-member-1.gif\" alt=\"Implementing missing Static Member\" width=\"700\"><\/figure>\n\n\n\n<p>When you want to override the default implementation of a member, you can call the <em>Generate Code<\/em> action and choose <em><a href=\"https:\/\/www.jetbrains.com\/help\/rider\/Code_Generation__Implementing_Overriding_Methods.html\" target=\"_blank\" rel=\"noreferrer noopener\">Overriding Members<\/a><\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/03\/csharp11-interface-static-virtual-member.png\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/03\/csharp11-interface-static-virtual-member.gif\" alt=\"Overriding a Static Virtual Member\" width=\"700\"><\/figure>\n\n\n\n<p>Maybe you&#8217;ve already spotted a tiny little detail we&#8217;ve added. A member that has a default implementation will show a little plus indicator on the usual <a href=\"https:\/\/www.jetbrains.com\/help\/rider\/Navigation_and_Search__Inheritance_Navigation.html\" target=\"_blank\" rel=\"noreferrer noopener\">gutter mark hierarchy icon<\/a>:<\/p>\n\n\n\n<figure class=\"wp-block-image is-resized is-style-default\"><img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/03\/csharp11-interface-static-virtual-member-navi.png\" alt=\"Default Implementatin Indicator in Gutter Mark Icon\" width=\"500\"><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Generic Attributes<\/h2>\n\n\n\n<p><strong>Attributes couldn&#8217;t define generic parameters up until C# 10<\/strong>. The lack of generic attributes has been frustrating for many .NET developers. Maybe even surprisingly, this does not just apply to the attribute usage but also to intermediate type declarations:<\/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=\"\">\/\/ Forbidden prior to C# 11\nclass MyAttribute : MyAttributeBase&lt;OtherType> { }\nclass MyAttributeBase&lt;T> : Attribute { }\n\n\/\/ Definitely forbidden prior to C# 11\n[MyAttribute&lt;OtherType>]\nclass MyClass { }<\/pre>\n\n\n\n<p>For most developers, the workaround has been to pass the type via <code>typeof<\/code>, either as a constructor or named argument. Unfortunately, this is rather verbose and also prevents you from using <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/generics\/constraints-on-type-parameters\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">generic constraints<\/a> for better static analysis:<\/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=\"\">class SerializationAttribute : Attribute\n{\n    public Type Type { get; set; }\n}\n\n[Serialization(Type = typeof(JsonSerializer))]\nclass Person { }<\/pre>\n\n\n\n<p><strong>In C# 11, this restriction has been lifted<\/strong> and you can now use generic types as with any other type:<\/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=\"\">class SerializationAttribute&lt;T> : Attribute\n    where T : ISerializer\n{\n}\n\n[Serialization&lt;JsonSerializer>]\nclass Person { }<\/pre>\n\n\n\n<p><strong>ReSharper and Rider are lifting this restriction as well<\/strong> and all existing features should work as expected with them.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Auto-Default Structs<\/h2>\n\n\n\n<p>Before C# 11, you were <strong>required to initialize all members in the constructor<\/strong> of a <code>struct<\/code> before you could call any instance methods or return from the constructor:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"14-15\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct Credentials\n{\n    public string? Username;\n    public string? Password;\n\n    public Credentials(string username, string password)\n    {\n        Username = username;\n        Password = password;\n    }\n\n    public Credentials(string token)\n    {\n        \/\/ Previously required:\n        \/\/ Username = null;\n        Password = token;\n    }\n}<\/pre>\n\n\n\n<p><strong>In C# 11, you <em>no longer<\/em> need to initialize all members in the constructor of a struc<\/strong>t<strong>.<\/strong> Members will use the default value if not set, just as with classes. This makes it easier to change classes to structs and vice versa.<\/p>\n\n\n\n<p><strong>ReSharper and Rider understand auto-default structs<\/strong> and will no longer show the <em>old<\/em> <em>warning<\/em> whenever you&#8217;re targeting the latest language version. Furthermore, we updated our <a href=\"https:\/\/www.jetbrains.com\/help\/resharper\/Code_Generation__Type_Constructors.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Generate | Constructor<\/em> action<\/a> to omit the <code>this()<\/code> constructor initializer when possible:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"7-8\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">struct Credentials\n{\n    public string? Username;\n    public string? Password;\n\n    public Credentials(string password)\n        \/\/ Only generated when necessary\n        \/\/ : this()\n    {\n        Password = password;\n    }\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Static interface members break the old rule of interfaces only being able to define instance members. They are particularly useful for defining custom numeric types, but can also help to reduce allocations throughout your codebase. Generic attributes lift a similar limitation and you no longer have to use <code>typeof<\/code> anymore. Auto-default structs remove some of the boilerplate code of initializing members with default values.<\/p>\n\n\n\n<p>Let us know in the comments if you have more ideas for additional features in ReSharper and Rider. We hope you liked this series of diving into C# 11 features and that you&#8217;ve learned how ReSharper and Rider can help with these!<\/p>\n","protected":false},"author":553,"featured_media":329899,"comment_status":"closed","ping_status":"closed","template":"","categories":[4992,1401],"tags":[158,7073,1398],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/326405"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/dotnet"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/553"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=326405"}],"version-history":[{"count":9,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/326405\/revisions"}],"predecessor-version":[{"id":332717,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/326405\/revisions\/332717"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/329899"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=326405"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=326405"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=326405"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=326405"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}