{"id":385369,"date":"2024-06-29T06:04:24","date_gmt":"2024-06-29T06:04:24","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=385369"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T06:04:24","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=385369","title":{"rendered":"<span>What&#8217;s new in C# 10: overview<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This article covers the new version of the C# language &#8212; C# 10. Compared to C# 9, C# 10 includes a short list of enhancements. Below we described the enhancements and added explanatory code fragments. Let&#8217;s look at them.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f8d\/1a2\/74c\/f8d1a274c3a5aa05c754821176f7c649.png\" width=\"580\" height=\"242\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f8d\/1a2\/74c\/f8d1a274c3a5aa05c754821176f7c649.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>Enhancements of structure types<\/h3>\n<h4>Initialization of field structure<\/h4>\n<p>Now you can set initialization of fields and properties in structures:<\/p>\n<pre><code class=\"cs\">public struct User {     public User(string name, int age)     {         Name = name;         Age = age;     }     string Name { get; set; } = string.Empty;     int Age { get; set; } = 18; } <\/code><\/pre>\n<h4>Parameterless constructor declaration in a structure type<\/h4>\n<p>Beginning with C# 10, you can declare a parameterless constructor in structures:<\/p>\n<pre><code class=\"cs\">public struct User {     public User()     {      }      public User(string name, int age)     {         Name = name;         Age = age;     }      string Name { get; set; } = string.Empty;     int Age { get; set; } = 18; } <\/code><\/pre>\n<p><strong>Important.<\/strong> You can use parameterless constructors only if all fields and\/or properties have initializers. For example, if you do not set the <em>Age<\/em> initializer, a compiler will issue an error:<\/p>\n<p><em>Error CS0843: Auto-implemented property &#8216;User.Age&#8217; must be fully assigned before control is returned to the caller.<\/em><\/p>\n<h3>Applying the with expression to a structure<\/h3>\n<p>Before, you could use the <em>with<\/em> expression with records. With C#10, you can use this expression with structures. Example:<\/p>\n<pre><code class=\"cs\">public struct User {     public User()     {      }      public User(string name, int age)     {         Name = name;         Age = age;     }      public string Name { get; set; } = string.Empty;     public int Age { get; set; } = 18; }  User myUser = new(\"Chris\", 21); User otherUser = myUser with { Name = \"David\" }; <\/code><\/pre>\n<p>It is clear that the property that we are changing (in this case, the <em>Name<\/em> field) must have a public access modifier.<\/p>\n<h3>Global using<\/h3>\n<p>Beginning with C# 10, you can use the <em>using<\/em> directive across an entire project. Add the <em>global<\/em> keyword before the <em>using<\/em> phrase:<\/p>\n<pre><code class=\"cs\">global using \"Library name\" <\/code><\/pre>\n<p>Thus, the <em>using<\/em> directive allows you not to duplicate the same namespaces in different files.<\/p>\n<p><strong>Important.<\/strong> Use <em>global using<\/em> construction BEFORE code lines that include <em>using<\/em> without <em>global<\/em> keyword. Example:<\/p>\n<pre><code class=\"cs\">global using System.Text; using System; using System.Linq; using System.Threading.Tasks; \/\/ Correct code fragment<\/code><\/pre>\n<p>Otherwise:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks; global using System.Text; \/\/ Error CS8915 \/\/ A global using directive must precede \/\/ all non-global using directives.<\/code><\/pre>\n<p>If you wrote the namespace that was previously written with the <em>global<\/em> keyword, the IDE will warn you (<em>IDE: 0005: Using directive is unnecessary<\/em>).<\/p>\n<h3>File-scoped namespace<\/h3>\n<p>Sometimes you need to use the namespace within the entire file. This action may shift the tabs to the right. To avoid this problem, you can now use the <em>namespace<\/em> keyword. Write the <em>namespace<\/em> keyword without braces:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks;  namespace TestC10;  public class TestClass {     .... } <\/code><\/pre>\n<p>Before C# 10, it was necessary to keep <em>namespace<\/em> braces open on the entire file:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks;  namespace TestC10 {     public class TestClass     {         ....     } } <\/code><\/pre>\n<p>Clearly, you can declare only one <em>namespace<\/em> in the file. Accordingly, the following code fragment is incorrect:<\/p>\n<pre><code class=\"cs\">namespace TestC10; namespace MyDir; \/\/ Error CS8954 \/\/ Source file can only contain \/\/ one file-scoped namespace declaration <\/code><\/pre>\n<p>as well as the following piece of code:<\/p>\n<pre><code class=\"cs\">namespace TestC10; namespace MyDir {     .... } \/\/ Error CS8955 \/\/ Source file can not contain both \/\/ file-scoped and normal namespace declarations.<\/code><\/pre>\n<h3>Record enhancements<\/h3>\n<h4>The class keyword<\/h4>\n<p>C# 10.0 introduces the optional keyword &#8212; <em>class<\/em>. The class keyword helps understand whether a record is of a reference type.<\/p>\n<p>Therefore, the two following records are identical:<\/p>\n<pre><code class=\"cs\">public record class Test(string Name, string Surname); public record Test(string Name, string Surname);<\/code><\/pre>\n<h4>Record structs<\/h4>\n<p>Now it&#8217;s possible to create record structs:<\/p>\n<pre><code class=\"cs\">record struct Test(string Name, string Surname)<\/code><\/pre>\n<p>By default, the properties of the <em>record struct<\/em> are mutable, unlike the standard <em>record<\/em> that have <em>init<\/em> modifier.<\/p>\n<pre><code class=\"cs\">string Name { get; set; } string Surname { get; set; }<\/code><\/pre>\n<p>We can set the <em>readonly<\/em> property to the record struct. Then access to the fields will be equivalent to the standard record:<\/p>\n<pre><code class=\"cs\">readonly record struct Test(string Name, string Surname);<\/code><\/pre>\n<p>where the properties are written as:<\/p>\n<pre><code class=\"cs\">string Name { get; init; } string Surname { get; init; }<\/code><\/pre>\n<p>The equality of two record struct objects is similar to the equality of two structs. Equality is true if these two objects store the same values:<\/p>\n<pre><code class=\"cs\">var firstRecord = new Person(\"Nick\", \"Smith\"); var secondRecord = new Person(\"Robert\", \"Smith\"); var thirdRecord = new Person(\"Nick\", \"Smith\");  Console.WriteLine(firstRecord == secondRecord); \/\/ False Console.WriteLine(firstRecord == thirdRecord); \/\/ True<\/code><\/pre>\n<p>Note that the compiler doesn&#8217;t synthesize a copy constructor for record struct types. If we create a copy constructor and use the <em>with<\/em> keyword when initializing a new object, then the assignment operator will be called instead of the copy constructor (as it happens when working with the <em>record class<\/em>).<\/p>\n<h4>Seal the ToString() method on records<\/h4>\n<p>As my colleague wrote in the article on the <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0860\/\">enhancements for C# 9<\/a> , records have the overridden <em>toString<\/em> method. There is an interesting point about inheritance as related to this method. The child objects cannot inherit the overriden <em>toString<\/em> method from the parent record. C# 10 introduces the <em>sealed<\/em> keyword so that the child objects can inherit the <em>ToString<\/em> method. This keyword prevents the compiler from synthesizing the <em>ToString<\/em> implementation for any derived records. Use the following keyword to override the <em>ToString<\/em> method:<\/p>\n<pre><code class=\"cs\">public sealed override string ToString() {     .... }<\/code><\/pre>\n<p>Let&#8217;s create a record that tries to override the <em>toString<\/em> method:<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname) {     public override string ToString()     {         return $\"{name} {surname}\";     } }<\/code><\/pre>\n<p>Now let&#8217;s inherit the second record:<\/p>\n<pre><code class=\"cs\">public record InheritedRecord : TestRec {     public InheritedRecord(string name, string surname)     :base(name, surname)     {      } }<\/code><\/pre>\n<p>Now let&#8217;s create an instance of each record and type the result to the console:<\/p>\n<pre><code class=\"cs\">TestRec myObj = new(\"Alex\", \"Johnson\"); Console.WriteLine(myObj.ToString()); \/\/ Alex Johnson  InheritedRecord mySecObj = new(\"Thomas\", \"Brown\"); Console.WriteLine(mySecObj.ToString()); \/\/ inheritedRecord { name = Thomas, surname = Brown}<\/code><\/pre>\n<p>As we can see, the <em>InheritedRecord<\/em> did not inherit the <em>toString<\/em> method.<\/p>\n<p>Let&#8217;s slightly change the <em>TestRec<\/em> record and add the <em>sealed<\/em> keyword:<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname) {     public sealed override string ToString()     {         return $\"{name} {surname}\";     } }<\/code><\/pre>\n<p>Now let&#8217;s re-create two instances of the records and type the result to the console:<\/p>\n<pre><code class=\"cs\">TestRec myObj = new(\"Alex\", \"Johnson\"); Console.WriteLine(myObj.ToString()); \/\/ Alex Johnson  InheritedRecord mySecObj = new(\"Thomas\", \"Brown\"); Console.WriteLine(mySecObj.ToString()); \/\/ Thomas Brown<\/code><\/pre>\n<p>And.. woohoo! The <em>InheritedRecord<\/em> inherited the <em>toString<\/em> method from the <em>TestRec<\/em>.<\/p>\n<h3>Easier access to nested fields and properties of property patterns<\/h3>\n<p>C# 8.0 introduced the property pattern that allows you to easily match on fields and\/or properties of an object with the necessary expressions.<\/p>\n<p>Before, if you needed to check any nested property, the code could look too cumbersome: <\/p>\n<pre><code class=\"cs\">....{property: {subProperty: pattern}}....<\/code><\/pre>\n<p>With C#10, you just need to add the dots between the properties:<\/p>\n<pre><code class=\"cs\">....{property.subProperty: pattern}....<\/code><\/pre>\n<p>Let&#8217;s see the change using the example of the method of taking the first 4 symbols of the name.<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname);  string TakeFourSymbols(TestRec obj) => obj switch {     \/\/ old way:     \/\/TestRec { name: {Length: > 4} } rec => rec.name.Substring(0,4),      \/\/ new way:     TestRec { name.Length: > 4 } rec => rec.name.Substring(0,4),     TestRec rec => rec.name, };<\/code><\/pre>\n<p>The example above shows that the new type of property access is simpler and clearer than before.<\/p>\n<h3>Constant interpolated strings<\/h3>\n<p>Before, this feature was not supported. C# 10 allows you to use string interpolation for constant strings:<\/p>\n<pre><code class=\"cs\">const string constStrFirst = \"FirstStr\"; const string summaryConstStr = $\"SecondStr {constStrFirst}\";<\/code><\/pre>\n<p><strong>Interesting fact.<\/strong> This change relates only to string interpolation for constant strings, i.e the addition of a constant character is not allowed:<\/p>\n<pre><code class=\"cs\">const char a = 'a'; const string constStrFirst = \"FirstStr\"; const string summaryConstStr = $\"SecondStr {constStrFirst} {a}\"; \/\/ Error CS0133 \/\/ The expression being assigned to \/\/ 'summaryConstStr' must be constant<\/code><\/pre>\n<h3>Assignment and declaration in same deconstruction<\/h3>\n<p>In earlier versions of C#, a deconstruction could assign values to EITHER declared variables (all are declared), OR variables that we initialize during calling (all are NOT declared):<\/p>\n<pre><code class=\"cs\">Car car = new(\"VAZ 2114\", \"Blue\");  var (model, color) = car; \/\/ Initialization  string model = string.Empty; string color = string.Empty; (model, color) = car; \/\/ Assignment<\/code><\/pre>\n<p>The new version of the language allows simultaneous use of both previously declared and undeclared variables in deconstruction:<\/p>\n<pre><code>string model = string.Empty; (model, var color) = car; \/\/ Initialization and assignment <\/code><\/pre>\n<p>The following error occurred in the C#9 version:<\/p>\n<p><em>Error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side.<\/em><\/p>\n<h3>Conclusion<\/h3>\n<p>As mentioned earlier, the list of changes is not as large as in the C#9 version. Some changes simplify the work, while others provide previously unavailable features. The C# is still evolving. We&#8217;re looking forward for new updates of the C# language.<\/p>\n<p>Haven&#8217;t read about new C# 9 features yet? Check them out in our separate <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0860\/\">article<\/a>.<\/p>\n<p>If you want to see the original source, you can read the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/whats-new\/csharp-10\">Microsoft documentation<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/584486\/\"> https:\/\/habr.com\/ru\/articles\/584486\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This article covers the new version of the C# language &#8212; C# 10. Compared to C# 9, C# 10 includes a short list of enhancements. Below we described the enhancements and added explanatory code fragments. Let&#8217;s look at them.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h3>Enhancements of structure types<\/h3>\n<h4>Initialization of field structure<\/h4>\n<p>Now you can set initialization of fields and properties in structures:<\/p>\n<pre><code class=\"cs\">public struct User {     public User(string name, int age)     {         Name = name;         Age = age;     }     string Name { get; set; } = string.Empty;     int Age { get; set; } = 18; } <\/code><\/pre>\n<h4>Parameterless constructor declaration in a structure type<\/h4>\n<p>Beginning with C# 10, you can declare a parameterless constructor in structures:<\/p>\n<pre><code class=\"cs\">public struct User {     public User()     {      }      public User(string name, int age)     {         Name = name;         Age = age;     }      string Name { get; set; } = string.Empty;     int Age { get; set; } = 18; } <\/code><\/pre>\n<p><strong>Important.<\/strong> You can use parameterless constructors only if all fields and\/or properties have initializers. For example, if you do not set the <em>Age<\/em> initializer, a compiler will issue an error:<\/p>\n<p><em>Error CS0843: Auto-implemented property &#8216;User.Age&#8217; must be fully assigned before control is returned to the caller.<\/em><\/p>\n<h3>Applying the with expression to a structure<\/h3>\n<p>Before, you could use the <em>with<\/em> expression with records. With C#10, you can use this expression with structures. Example:<\/p>\n<pre><code class=\"cs\">public struct User {     public User()     {      }      public User(string name, int age)     {         Name = name;         Age = age;     }      public string Name { get; set; } = string.Empty;     public int Age { get; set; } = 18; }  User myUser = new(\"Chris\", 21); User otherUser = myUser with { Name = \"David\" }; <\/code><\/pre>\n<p>It is clear that the property that we are changing (in this case, the <em>Name<\/em> field) must have a public access modifier.<\/p>\n<h3>Global using<\/h3>\n<p>Beginning with C# 10, you can use the <em>using<\/em> directive across an entire project. Add the <em>global<\/em> keyword before the <em>using<\/em> phrase:<\/p>\n<pre><code class=\"cs\">global using \"Library name\" <\/code><\/pre>\n<p>Thus, the <em>using<\/em> directive allows you not to duplicate the same namespaces in different files.<\/p>\n<p><strong>Important.<\/strong> Use <em>global using<\/em> construction BEFORE code lines that include <em>using<\/em> without <em>global<\/em> keyword. Example:<\/p>\n<pre><code class=\"cs\">global using System.Text; using System; using System.Linq; using System.Threading.Tasks; \/\/ Correct code fragment<\/code><\/pre>\n<p>Otherwise:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks; global using System.Text; \/\/ Error CS8915 \/\/ A global using directive must precede \/\/ all non-global using directives.<\/code><\/pre>\n<p>If you wrote the namespace that was previously written with the <em>global<\/em> keyword, the IDE will warn you (<em>IDE: 0005: Using directive is unnecessary<\/em>).<\/p>\n<h3>File-scoped namespace<\/h3>\n<p>Sometimes you need to use the namespace within the entire file. This action may shift the tabs to the right. To avoid this problem, you can now use the <em>namespace<\/em> keyword. Write the <em>namespace<\/em> keyword without braces:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks;  namespace TestC10;  public class TestClass {     .... } <\/code><\/pre>\n<p>Before C# 10, it was necessary to keep <em>namespace<\/em> braces open on the entire file:<\/p>\n<pre><code class=\"cs\">using System; using System.Linq; using System.Threading.Tasks;  namespace TestC10 {     public class TestClass     {         ....     } } <\/code><\/pre>\n<p>Clearly, you can declare only one <em>namespace<\/em> in the file. Accordingly, the following code fragment is incorrect:<\/p>\n<pre><code class=\"cs\">namespace TestC10; namespace MyDir; \/\/ Error CS8954 \/\/ Source file can only contain \/\/ one file-scoped namespace declaration <\/code><\/pre>\n<p>as well as the following piece of code:<\/p>\n<pre><code class=\"cs\">namespace TestC10; namespace MyDir {     .... } \/\/ Error CS8955 \/\/ Source file can not contain both \/\/ file-scoped and normal namespace declarations.<\/code><\/pre>\n<h3>Record enhancements<\/h3>\n<h4>The class keyword<\/h4>\n<p>C# 10.0 introduces the optional keyword &#8212; <em>class<\/em>. The class keyword helps understand whether a record is of a reference type.<\/p>\n<p>Therefore, the two following records are identical:<\/p>\n<pre><code class=\"cs\">public record class Test(string Name, string Surname); public record Test(string Name, string Surname);<\/code><\/pre>\n<h4>Record structs<\/h4>\n<p>Now it&#8217;s possible to create record structs:<\/p>\n<pre><code class=\"cs\">record struct Test(string Name, string Surname)<\/code><\/pre>\n<p>By default, the properties of the <em>record struct<\/em> are mutable, unlike the standard <em>record<\/em> that have <em>init<\/em> modifier.<\/p>\n<pre><code class=\"cs\">string Name { get; set; } string Surname { get; set; }<\/code><\/pre>\n<p>We can set the <em>readonly<\/em> property to the record struct. Then access to the fields will be equivalent to the standard record:<\/p>\n<pre><code class=\"cs\">readonly record struct Test(string Name, string Surname);<\/code><\/pre>\n<p>where the properties are written as:<\/p>\n<pre><code class=\"cs\">string Name { get; init; } string Surname { get; init; }<\/code><\/pre>\n<p>The equality of two record struct objects is similar to the equality of two structs. Equality is true if these two objects store the same values:<\/p>\n<pre><code class=\"cs\">var firstRecord = new Person(\"Nick\", \"Smith\"); var secondRecord = new Person(\"Robert\", \"Smith\"); var thirdRecord = new Person(\"Nick\", \"Smith\");  Console.WriteLine(firstRecord == secondRecord); \/\/ False Console.WriteLine(firstRecord == thirdRecord); \/\/ True<\/code><\/pre>\n<p>Note that the compiler doesn&#8217;t synthesize a copy constructor for record struct types. If we create a copy constructor and use the <em>with<\/em> keyword when initializing a new object, then the assignment operator will be called instead of the copy constructor (as it happens when working with the <em>record class<\/em>).<\/p>\n<h4>Seal the ToString() method on records<\/h4>\n<p>As my colleague wrote in the article on the <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0860\/\">enhancements for C# 9<\/a> , records have the overridden <em>toString<\/em> method. There is an interesting point about inheritance as related to this method. The child objects cannot inherit the overriden <em>toString<\/em> method from the parent record. C# 10 introduces the <em>sealed<\/em> keyword so that the child objects can inherit the <em>ToString<\/em> method. This keyword prevents the compiler from synthesizing the <em>ToString<\/em> implementation for any derived records. Use the following keyword to override the <em>ToString<\/em> method:<\/p>\n<pre><code class=\"cs\">public sealed override string ToString() {     .... }<\/code><\/pre>\n<p>Let&#8217;s create a record that tries to override the <em>toString<\/em> method:<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname) {     public override string ToString()     {         return $\"{name} {surname}\";     } }<\/code><\/pre>\n<p>Now let&#8217;s inherit the second record:<\/p>\n<pre><code class=\"cs\">public record InheritedRecord : TestRec {     public InheritedRecord(string name, string surname)     :base(name, surname)     {      } }<\/code><\/pre>\n<p>Now let&#8217;s create an instance of each record and type the result to the console:<\/p>\n<pre><code class=\"cs\">TestRec myObj = new(\"Alex\", \"Johnson\"); Console.WriteLine(myObj.ToString()); \/\/ Alex Johnson  InheritedRecord mySecObj = new(\"Thomas\", \"Brown\"); Console.WriteLine(mySecObj.ToString()); \/\/ inheritedRecord { name = Thomas, surname = Brown}<\/code><\/pre>\n<p>As we can see, the <em>InheritedRecord<\/em> did not inherit the <em>toString<\/em> method.<\/p>\n<p>Let&#8217;s slightly change the <em>TestRec<\/em> record and add the <em>sealed<\/em> keyword:<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname) {     public sealed override string ToString()     {         return $\"{name} {surname}\";     } }<\/code><\/pre>\n<p>Now let&#8217;s re-create two instances of the records and type the result to the console:<\/p>\n<pre><code class=\"cs\">TestRec myObj = new(\"Alex\", \"Johnson\"); Console.WriteLine(myObj.ToString()); \/\/ Alex Johnson  InheritedRecord mySecObj = new(\"Thomas\", \"Brown\"); Console.WriteLine(mySecObj.ToString()); \/\/ Thomas Brown<\/code><\/pre>\n<p>And.. woohoo! The <em>InheritedRecord<\/em> inherited the <em>toString<\/em> method from the <em>TestRec<\/em>.<\/p>\n<h3>Easier access to nested fields and properties of property patterns<\/h3>\n<p>C# 8.0 introduced the property pattern that allows you to easily match on fields and\/or properties of an object with the necessary expressions.<\/p>\n<p>Before, if you needed to check any nested property, the code could look too cumbersome: <\/p>\n<pre><code class=\"cs\">....{property: {subProperty: pattern}}....<\/code><\/pre>\n<p>With C#10, you just need to add the dots between the properties:<\/p>\n<pre><code class=\"cs\">....{property.subProperty: pattern}....<\/code><\/pre>\n<p>Let&#8217;s see the change using the example of the method of taking the first 4 symbols of the name.<\/p>\n<pre><code class=\"cs\">public record TestRec(string name, string surname);  string TakeFourSymbols(TestRec obj) => obj switch {     \/\/ old way:     \/\/TestRec { name: {Length: > 4} } rec => rec.name.Substring(0,4),      \/\/ new way:     TestRec { name.Length: > 4 } rec => rec.name.Substring(0,4),     TestRec rec => rec.name, };<\/code><\/pre>\n<p>The example above shows that the new type of property access is simpler and clearer than before.<\/p>\n<h3>Constant interpolated strings<\/h3>\n<p>Before, this feature was not supported. C# 10 allows you to use string interpolation for constant strings:<\/p>\n<pre><code class=\"cs\">const string constStrFirst = \"FirstStr\"; const string summaryConstStr = $\"SecondStr {constStrFirst}\";<\/code><\/pre>\n<p><strong>Interesting fact.<\/strong> This change relates only to string interpolation for constant strings, i.e the addition of a constant character is not allowed:<\/p>\n<pre><code class=\"cs\">const char a = 'a'; const string constStrFirst = \"FirstStr\"; const string summaryConstStr = $\"SecondStr {constStrFirst} {a}\"; \/\/ Error CS0133 \/\/ The expression being assigned to \/\/ 'summaryConstStr' must be constant<\/code><\/pre>\n<h3>Assignment and declaration in same deconstruction<\/h3>\n<p>In earlier versions of C#, a deconstruction could assign values to EITHER declared variables (all are declared), OR variables that we initialize during calling (all are NOT declared):<\/p>\n<pre><code class=\"cs\">Car car = new(\"VAZ 2114\", \"Blue\");  var (model, color) = car; \/\/ Initialization  string model = string.Empty; string color = string.Empty; (model, color) = car; \/\/ Assignment<\/code><\/pre>\n<p>The new version of the language allows simultaneous use of both previously declared and undeclared variables in deconstruction:<\/p>\n<pre><code>string model = string.Empty; (model, var color) = car; \/\/ Initialization and assignment <\/code><\/pre>\n<p>The following error occurred in the C#9 version:<\/p>\n<p><em>Error CS8184: A deconstruction cannot mix declarations and expressions on the left-hand-side.<\/em><\/p>\n<h3>Conclusion<\/h3>\n<p>As mentioned earlier, the list of changes is not as large as in the C#9 version. Some changes simplify the work, while others provide previously unavailable features. The C# is still evolving. We&#8217;re looking forward for new updates of the C# language.<\/p>\n<p>Haven&#8217;t read about new C# 9 features yet? Check them out in our separate <a href=\"https:\/\/pvs-studio.com\/en\/blog\/posts\/csharp\/0860\/\">article<\/a>.<\/p>\n<p>If you want to see the original source, you can read the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/csharp\/whats-new\/csharp-10\">Microsoft documentation<\/a>.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/584486\/\"> https:\/\/habr.com\/ru\/articles\/584486\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-385369","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385369","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=385369"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/385369\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=385369"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=385369"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=385369"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}