{"id":45186,"date":"2023-04-11T10:15:00","date_gmt":"2023-04-11T17:15:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=45186"},"modified":"2023-04-10T17:59:27","modified_gmt":"2023-04-11T00:59:27","slug":"check-out-csharp-12-preview","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/check-out-csharp-12-preview\/","title":{"rendered":"Check out new C# 12 preview features!"},"content":{"rendered":"<p>We&#8217;re excited to preview three new features for C# 12:<\/p>\n<ul>\n<li>Primary constructors for non-record classes and structs<\/li>\n<li>Using aliases for any type<\/li>\n<li>Default values for lambda expression parameters<\/li>\n<\/ul>\n<p>In addition to this overview, you can also find detailed documentation in the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12\">What\u2019s new in C# article on Microsoft Learn<\/a>. <\/p>\n<p>To test out these features yourself, you can download the latest  <a href=\"https:\/\/visualstudio.com\/preview\">Visual Studio 17.6 preview<\/a> or the latest <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/8.0\">.NET 8 preview<\/a>. Find out what else is coming for developers in <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-3\">Announcing .NET 8 Preview 3<\/a> and other posts on the .NET blog.<\/p>\n<h2>Primary constructors for non-record classes and structs<\/h2>\n<p>Primary constructors let you add parameters to the class declaration itself and use these values in the class body. For example, you could use the parameters to initialize properties or in the code of methods and property accessors. Primary constructors were introduced for records in C# 9 as part of the positional syntax for records. C# 12 extends them to all classes and structs.<\/p>\n<p>The basic syntax and usage for a primary constructor is:<\/p>\n<pre><code class=\"language-csharp\">public class Student(int id, string name, IEnumerable&lt;decimal&gt; grades)\r\n{\r\n    public Student(int id, string name) : this(id, name, Enumerable.Empty&lt;decimal&gt;()) { }\r\n    public int Id =&gt; id;\r\n    public string Name { get; set; } = name.Trim();\r\n    public decimal GPA =&gt; grades.Any() ? grades.Average() : 4.0m;\r\n} <\/code><\/pre>\n<p>The primary constructor parameters in the <code>Student<\/code> class above are available throughout the body of the class. One way you can use them is to initialize properties. Unlike records, properties are not automatically created for primary constructor parameters in non-record classes and structs. This reflects that non-record classes and structs often have more complexity than records, combining data and behavior. As a result, they often need constructor parameters that should not be exposed. Explicitly creating properties makes it obvious which data is exposed, consistent with the common use of classes. Primary constructors help avoid the boilerplate of declaring private fields and having trivial constructor bodies assign parameter values to those fields.<\/p>\n<p>When primary constructor parameters are used in methods or property accessors (the <code>grades<\/code> parameter in the <code>Student<\/code> class), they need to be <em>captured<\/em> in order for them to stay around after the constructor is done executing. This is similar to how parameters and local variables are captured in lambda expressions. For primary constructor parameters, the capture is implemented by generating a private backing field on the class or struct itself. The field has an \u201cunspeakable\u201d name, which means it will not collide with other naming and is not obvious via reflection. Consider how you assign and use primary constructor parameters to avoid double storage. For example, <code>name<\/code> is used to initialize the auto-property <code>Name<\/code>, which has its own backing field. If another member referenced the parameter <code>name<\/code> directly, it would also be stored in its own backing field, leading to an unfortunate duplication.<\/p>\n<p>A class with a primary constructor can have additional constructors. Additional constructors must use a <code>this(\u2026)<\/code> initializer to call another constructor on the same class or struct. This ensures that the primary constructor is always called and all the all the data necessary to create the class is present. A struct type always has a parameterless constructor. The implicit parameterless constructor doesn\u2019t use a <code>this()<\/code> initializer to call the primary constructor. In the case of a struct, you must write an explicit parameterless constructor to do if you want the primary constructor called.<\/p>\n<p>Find out more in the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12#primary-constructors\">What&#8217;s new in C# 12<\/a> article with links to all new content for primary constructors.<\/p>\n<p>You can leave feedback on primary constructors in the CSharpLang GitHub repository at <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7109\">Preview Feedback: C# 12 Primary constructors<\/a>.<\/p>\n<h2>Using directives for additional types<\/h2>\n<p>C# 12 extends using directive support to any type. Here are a few examples:<\/p>\n<pre><code class=\"language-csharp\">using Measurement = (string, int);\r\nusing PathOfPoints = int[];\r\nusing DatabaseInt = int?;<\/code><\/pre>\n<p>You can now alias almost any type. You can alias nullable value types, although you cannot alias nullable reference types. Tuples are particularly exciting because you can include element names and types:<\/p>\n<pre><code class=\"language-csharp\">using Measurement = (string Units, int Distance);<\/code><\/pre>\n<p>You can use aliases anywhere you would use a type. For example:<\/p>\n<pre><code class=\"language-csharp\">public void F(Measurement x)\r\n{ }<\/code><\/pre>\n<p>Aliasing types lets you abstract the actual types you are using and lets you give friendly names to confusing or long generic names. This can make it easier to read your code.<\/p>\n<p>Find out more in the <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12#alias-any-type\">What&#8217;s new in C# 12<\/a> article.<\/p>\n<p>You can leave feedback on aliases for any type in the CSharpLang GitHub repository at <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7110\">Preview Feedback: C# 12 Alias any type<\/a>.<\/p>\n<h2>Default values for lambda expressions<\/h2>\n<p>C# 12 takes the next step in empowering lambda expressions by letting you specify default values for parameters. The syntax is the same as for other default parameters: <\/p>\n<pre><code class=\"language-csharp\">var addWithDefault = (int addTo = 2) =&gt; addTo + 1;\r\naddWithDefault(); \/\/ 3\r\naddWithDefault(5); \/\/ 6<\/code><\/pre>\n<p>Similar to other default values, the default value will be emitted in metadata and is available via reflection as the <code>DefaultValue<\/code> of the <code>ParameterInfo<\/code> of the lambda&#8217;s <code>Method<\/code> property. For example:<\/p>\n<pre><code class=\"language-csharp\">var addWithDefault = (int addTo = 2) =&gt; addTo + 1;\r\naddWithDefault.Method.GetParameters()[0].DefaultValue; \/\/ 2<\/code><\/pre>\n<p>Prior to C# 12 you needed to use a local function or the unwieldy <code>DefaultParameterValue<\/code>\nfrom the <code>System.Runtime.InteropServices<\/code> namespace to provide a default value for lambda expression parameters. These approaches still work but are harder to read and are inconsistent with default values on methods. With the new default values on lambdas you\u2019ll have a consistent look for default parameter values on methods, constructors and lambda expressions.<\/p>\n<p>Find out more in the article on <a href=\"https:\/\/learn.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-12#default-lambda-parameters\">What&#8217;s new in C# 12<\/a>.<\/p>\n<p>You can leave feedback on default values for lambda parameters in the CSharpLang GitHub repository at <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7111\">Preview Feedback: C# 12 Default values in lambda expressions<\/a>.<\/p>\n<h2>Next steps<\/h2>\n<p>We hope you will download the preview and check out these features. We are experimenting in C# 12 with a dedicated issue for each feature. We4 hope this will focus feedback and make it easier for you to upvote what other people are saying. You can find these at <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7109\">Preview Feedback: C# 12 Primary constructors<\/a>, <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7110\">Preview Feedback: C# 12 Alias any type<\/a>, and <a href=\"https:\/\/github.com\/dotnet\/csharplang\/discussions\/7111\">Preview Feedback: C# 12 Default values in lambda expressions<\/a>. <\/p>\n<p>You can follow our implementation progress for C# 12 at <a href=\"https:\/\/github.com\/dotnet\/roslyn\/blob\/main\/docs\/Language%20Feature%20Status.md\">Roslyn Feature Status<\/a>. You can also follow the design process at the <a href=\"https:\/\/github.com\/dotnet\/csharplang\">CSharpLang GitHub repository<\/a> where you will find proposals, discussions and meeting notes.<\/p>\n<p>We look forward to hearing from you!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The first set of C# 12 features are here in preview including primary constructors, using aliases, and lambda expression parameters.<\/p>\n","protected":false},"author":1097,"featured_media":45187,"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-45186","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>The first set of C# 12 features are here in preview including primary constructors, using aliases, and lambda expression parameters.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/45186","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\/1097"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=45186"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/45186\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/45187"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=45186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=45186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=45186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}