{"id":37390,"date":"2021-11-08T08:00:09","date_gmt":"2021-11-08T15:00:09","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=37390"},"modified":"2021-11-09T15:47:19","modified_gmt":"2021-11-09T22:47:19","slug":"welcome-to-csharp-10","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/welcome-to-csharp-10\/","title":{"rendered":"Welcome to C# 10"},"content":{"rendered":"<p>Today, we are happy to announce the release of C# 10 as part of .NET 6 and Visual Studio 2022. In this post, we&#8217;re covering a lot of the new C# 10 features that make your code prettier, more expressive, and faster.<\/p>\n<p>Read the <a href=\"https:\/\/aka.ms\/vs2022gablog\">Visual Studio 2022 announcement<\/a> and the <a href=\"https:\/\/aka.ms\/dotnet6-GA\">.NET 6 announcement<\/a> to find out more, including how to install.<\/p>\n<h2>Global and implicit usings<\/h2>\n<p><code>using<\/code> directives simplify how you work with namespaces. C# 10 includes a new <code>global using<\/code> directive and <em>implicit usings<\/em> to reduce the number of usings you need to specify at the top of each file.<\/p>\n<h3>Global using directives<\/h3>\n<p>If the keyword <code>global<\/code> appears prior to a <code>using<\/code> directive, that using applies to the entire project:<\/p>\n<pre><code class=\"language-c#\">global using System;<\/code><\/pre>\n<p>You can use any feature of <code>using<\/code> within a <code>global using<\/code> directive. For example, adding <code>static<\/code> imports a type and makes the type&#8217;s members and nested types available throughout your project. If you use an alias in your using directive, that alias will also affect your entire project:<\/p>\n<pre><code class=\"language-c#\">global using static System.Console;\r\nglobal using Env = System.Environment;<\/code><\/pre>\n<p>You can put global usings in any <code>.cs<\/code> file, including <code>Program.cs<\/code> or a specifically named file like <code>globalusings.cs<\/code>. The scope of global usings is the current compilation, which generally corresponds to the current project.<\/p>\n<p>For more information, see <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/keywords\/using-directive#global-modifier\">global using directives<\/a>.<\/p>\n<h3>Implicit usings<\/h3>\n<p>The Implicit usings feature automatically adds common <code>global using<\/code> directives for the type of project you are building. To enable implicit usings set the <code>ImplicitUsings<\/code> property in your <code>.csproj<\/code> file:<\/p>\n<pre><code class=\"language-xml\">&lt;PropertyGroup&gt;\r\n    &lt;!-- Other properties like OutputType and TargetFramework --&gt;\r\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\r\n&lt;\/PropertyGroup&gt;<\/code><\/pre>\n<p>Implicit usings are enabled in the new .NET 6 templates. Read more about the changes to the .NET 6 templates at this <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-preview-7\/#net-sdk-c-project-templates-modernized\">blog post<\/a>.<\/p>\n<p>The specific set of <code>global using<\/code> directives included depend on the type of application you are building. For example, implicit usings for a console application or a class library are different than those for an ASP.NET application.<\/p>\n<p>For more information, see this <a href=\"https:\/\/aka.ms\/csharp-implicit-usings\">implicit usings<\/a> article.<\/p>\n<h3>Combining using features<\/h3>\n<p>Traditional <code>using<\/code> directives at the top of your files, global <code>using<\/code> directives, and implicit usings work well together. Implicit usings let you include the .NET namespaces appropriate to the kind of project you&#8217;re building with a single line in your project file. <code>global using<\/code> directives let you include additional namespaces to make them available throughout your project. The <code>using<\/code> directives at the top of your code files let you include namespaces used by just a few files in your project.<\/p>\n<p>Regardless of how they are defined, extra <code>using<\/code> directives increase the possibility of ambiguity in name resolution. If you encounter this, consider adding an alias or reducing the number of namespaces you are importing. For example, you can replace <code>global using<\/code> directives with explicit using directives at the top of a subset of files.<\/p>\n<p>If you need to remove namespaces that have been included via implicit usings, you can specify them in your project file:<\/p>\n<pre><code class=\"language-xml\">&lt;ItemGroup&gt;\r\n  &lt;Using Remove=\"System.Threading.Tasks\" \/&gt;\r\n&lt;\/ItemGroup&gt;<\/code><\/pre>\n<p>You can also add namespace that behave as though they were <code>global using<\/code> directives, you can add <code>Using<\/code> items to your project file, for example:<\/p>\n<pre><code class=\"language-xml\">&lt;ItemGroup&gt;\r\n  &lt;Using Include=\"System.IO.Pipes\" \/&gt;\r\n&lt;\/ItemGroup&gt;<\/code><\/pre>\n<h2>File-scoped namespaces<\/h2>\n<p>Many files contain code for a single namespace. Starting in C# 10, you can include a namespace as a statement, followed by a semi-colon and without the curly brackets:<\/p>\n<pre><code class=\"language-c#\">namespace MyCompany.MyNamespace;\r\n\r\nclass MyClass \/\/ Note: no indentation\r\n{ ... } <\/code><\/pre>\n<p>This simplifies the code and removes a level of nesting. Only one file-scoped namespace declaration is allowed, and it must come before any types are declared.<\/p>\n<p>For more information about file-scoped namespaces, see the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/keywords\/namespace\">namespace keyword<\/a> article.<\/p>\n<h2>Improvements for lambda expressions and method groups<\/h2>\n<p>We&#8217;ve made several improvements to both the types and the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-10#lambda-expression-improvements\">syntax surrounding lambdas<\/a>. We expect these to be widely useful, and one of the driving scenarios has been to make <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-asp-net-core-in-net-6\/\">ASP.NET Minimal APIs<\/a> even more straightforward.<\/p>\n<h3>Natural types for lambdas<\/h3>\n<p>Lambda expressions now sometimes have a &#8220;natural&#8221; type. This means that the compiler can often infer the type of the lambda expression.<\/p>\n<p>Up until now a lambda expression had to be converted to a delegate or an expression type. For most purposes you&#8217;d use one of the overloaded <code>Func&lt;...&gt;<\/code> or <code>Action&lt;...&gt;<\/code> delegate types in the BCL:<\/p>\n<pre><code class=\"language-c#\">Func&lt;string, int&gt; parse = (string s) =&gt; int.Parse(s);<\/code><\/pre>\n<p>Starting with C# 10, however, if a lambda does not have such a &#8220;target type&#8221; we will try to compute one for you:<\/p>\n<pre><code class=\"language-c#\">var parse = (string s) =&gt; int.Parse(s);<\/code><\/pre>\n<p>You can hover over <code>var parse<\/code> in your favorite editor and see that the type is still <code>Func&lt;string, int&gt;<\/code>. In general, the compiler will use an available <code>Func<\/code> or <code>Action<\/code> delegate, if a suitable one exists. Otherwise, it will synthesize a delegate type (for example, when you have <code>ref<\/code> parameters or have a large number of parameters).<\/p>\n<p>Not all lambdas have natural types &#8211; some just don&#8217;t have enough type information. For instance, leaving off parameter types will leave the compiler unable to decide which delegate type to use:<\/p>\n<pre><code class=\"language-c#\">var parse = s =&gt; int.Parse(s); \/\/ ERROR: Not enough type info in the lambda<\/code><\/pre>\n<p>The natural type of lambdas means that they can be assigned to a weaker type, such as <code>object<\/code> or <code>Delegate<\/code>:<\/p>\n<pre><code class=\"language-c#\">object parse = (string s) =&gt; int.Parse(s);   \/\/ Func&lt;string, int&gt;\r\nDelegate parse = (string s) =&gt; int.Parse(s); \/\/ Func&lt;string, int&gt;<\/code><\/pre>\n<p>When it comes to expression trees we do a combination of &#8220;target&#8221; and &#8220;natural&#8221; typing. If the target type is <code>LambdaExpression<\/code> or the non-generic <code>Expression<\/code> (base type for all expression trees) <em>and<\/em> the lambda has a natural delegate type <code>D<\/code> we will instead produce an <code>Expression&lt;D&gt;<\/code>:<\/p>\n<pre><code class=\"language-c#\">LambdaExpression parseExpr = (string s) =&gt; int.Parse(s); \/\/ Expression&lt;Func&lt;string, int&gt;&gt;\r\nExpression parseExpr = (string s) =&gt; int.Parse(s);       \/\/ Expression&lt;Func&lt;string, int&gt;&gt;\r\n<\/code><\/pre>\n<h3>Natural types for method groups<\/h3>\n<p>Method groups (that is, method names without argument lists) now also sometimes have a natural type. You have always been able to convert a method group to a compatible delegate type:<\/p>\n<pre><code class=\"language-c#\">Func&lt;int&gt; read = Console.Read;\r\nAction&lt;string&gt; write = Console.Write;<\/code><\/pre>\n<p>Now, if the method group has just one overload it will have a natural type:<\/p>\n<pre><code class=\"language-c#\">var read = Console.Read; \/\/ Just one overload; Func&lt;int&gt; inferred\r\nvar write = Console.Write; \/\/ ERROR: Multiple overloads, can't choose<\/code><\/pre>\n<h3>Return types for lambdas<\/h3>\n<p>In the previous examples, the return type of the lambda expression was obvious and was just being inferred. That isn&#8217;t always the case:<\/p>\n<pre><code class=\"language-c#\">var choose = (bool b) =&gt; b ? 1 : \"two\"; \/\/ ERROR: Can't infer return type<\/code><\/pre>\n<p>In C# 10, you can specify an explicit return type on a lambda expression, just like you do on a method or a local function. The return type goes right before the parameters. When you specify an explicit return type, the parameters must be parenthesized, so that it&#8217;s not too confusing to the compiler or other developers:<\/p>\n<pre><code class=\"language-c#\">var choose = object (bool b) =&gt; b ? 1 : \"two\"; \/\/ Func&lt;bool, object&gt;<\/code><\/pre>\n<h3>Attributes on lambdas<\/h3>\n<p>Starting in C# 10, you can put attributes on lambda expressions in the same way you do for methods and local functions. They go right where you expect; at the beginning. Once again, the lambda&#8217;s parameter list must be parenthesized when there are attributes:<\/p>\n<pre><code class=\"language-c#\">Func&lt;string, int&gt; parse = [Example(1)] (s) =&gt; int.Parse(s);\r\nvar choose = [Example(2)][Example(3)] object (bool b) =&gt; b ? 1 : \"two\";<\/code><\/pre>\n<p>Just like local functions, attributes can be applied to lambdas if they are valid on <code>AttributeTargets.Method<\/code>.<\/p>\n<p>Lambdas are invoked differently than methods and local functions, and as a result attributes do not have any effect when the lambda is invoked. However, attributes on lambdas are still useful for code analysis, and they are also emitted on the methods that the compiler generates under the hood for lambdas, so they can be discovered via reflection.<\/p>\n<h2>Improvements to structs<\/h2>\n<p>C# 10 introduces features for structs that provide better parity between structs and classes. These new features include parameterless constructors, field initializers, record structs and <code>with<\/code> expressions.<\/p>\n<h3>Parameterless struct constructors and field initializers<\/h3>\n<p>Prior to C# 10, every struct had an implicit public parameterless constructor that set the struct&#8217;s fields to <code>default<\/code>. It was an error for you to create a parameterless constructor on a struct.<\/p>\n<p>Starting in C# 10, you can include your own parameterless struct constructors. If you don\u2019t supply one, the implicit parameterless constructor will be supplied to set all fields to their default. Parameterless constructors you create in structs must be public and cannot be partial:<\/p>\n<pre><code class=\"language-c#\">public struct Address\r\n{\r\n    public Address()\r\n    {\r\n        City = \"&lt;unknown&gt;\";\r\n    }\r\n    public string City { get; init; }\r\n}\r\n<\/code><\/pre>\n<p>You can initialize fields in a parameterless constructor as above, or you can initialize them via field or property initializers:<\/p>\n<pre><code class=\"language-c#\">public struct Address\r\n{\r\n    public string City { get; init; } = \"&lt;unknown&gt;\";\r\n}<\/code><\/pre>\n<p>Structs that are created via <code>default<\/code> or as part of array allocation ignore explicit parameterless constructors, and always set struct members to their default values. For more information about parameterless constructors in structs, see the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/builtin-types\/struct#parameterless-constructors-and-field-initializers\">struct type<\/a>.<\/p>\n<h3>record structs<\/h3>\n<p>Starting in C# 10, records can now be defined with <code>record struct<\/code>. These are similar to record classes that were introduced in C# 9:<\/p>\n<pre><code class=\"language-c#\">public record struct Person\r\n{\r\n    public string FirstName { get; init; }\r\n    public string LastName { get; init; }\r\n}<\/code><\/pre>\n<p>You can continue to define record classes with <code>record<\/code>, or you can use <code>record class<\/code> for clarity.<\/p>\n<p>Structs already had value equality &#8211; when you compare them it is by value. Record structs add <code>IEquatable&lt;T&gt;<\/code> support and the <code>==<\/code> operator. Record structs provide a custom implementation of <code>IEquatable&lt;T&gt;<\/code> to avoid the performance issues of reflection, and they include record features like a <code>ToString()<\/code> override.<\/p>\n<p>Record structs can be <em>positional<\/em>, with a primary constructor implicitly declaring public members:<\/p>\n<pre><code class=\"language-c#\">public record struct Person(string FirstName, string LastName);<\/code><\/pre>\n<p>The parameters of the primary constructor become public auto-implemented properties of the record struct. Unlike record classes, the implicitly created properties are read\/write. This makes it easier to convert tuples to named types. Changing return types from a tuple like <code>(string FirstName, string LastName)<\/code> to a named type of <code>Person<\/code> can clean up your code and guarantee consistent member names. Declaring the positional record struct is easy and keeps the mutable semantics.<\/p>\n<p>If you declare a property or field with the same name as a primary constructor parameter, no auto-property will be synthesized and yours will be used.<\/p>\n<p>To create an immutable record struct, add <code>readonly<\/code> to the struct (as you can to any struct) or apply <code>readonly<\/code> to individual properties. Object initializers are part of the construction phase where readonly properties can be set. Here is just one of the ways you can work with immutable record structs:<\/p>\n<pre><code class=\"language-c#\">var person = new Person { FirstName = \"Mads\", LastName = \"Torgersen\"};\r\n\r\npublic readonly record struct Person\r\n{\r\n    public string FirstName { get; init; }\r\n    public string LastName { get; init; }\r\n}<\/code><\/pre>\n<p>Find out more about <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/builtin-types\/record\">record structs in this article<\/a>.<\/p>\n<h3><code>sealed<\/code> modifier on <code>ToString()<\/code> in record classes<\/h3>\n<p>Record classes have also been improved. Starting in C# 10 the <code>ToString()<\/code> method can include the sealed modifier, which prevents the compiler from synthesizing a <code>ToString<\/code> implementation for any derived records.<\/p>\n<p>Find out more about <code>ToString()<\/code> in records <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/builtin-types\/record#built-in-formatting-for-display\">in this article<\/a>.<\/p>\n<h3><code>with<\/code> expressions on structs and anonymous types<\/h3>\n<p>C# 10 supports <code>with<\/code> expressions for all structs, including record structs, as well as for anonymous types:<\/p>\n<pre><code class=\"language-c#\">var person2 = person with { LastName = \"Kristensen\" };<\/code><\/pre>\n<p>This returns a new instance with the new value. You can update any number of values. Values you do not set will retain the same value as the initial instance.<\/p>\n<p>Learn more about <code>with<\/code> <a href=\"https:\/\/docs.microsoft.com\/\/dotnet\/csharp\/language-reference\/operators\/with-expression\">in this article<\/a><\/p>\n<h2>Interpolated string improvements<\/h2>\n<p>When we added interpolated strings to C#, we always felt that there was more that could be done with that syntax down the line, both for performance and expressiveness. With C# 10, that time has come!<\/p>\n<h3>Interpolated string handlers<\/h3>\n<p>Today the compiler turns interpolated strings into a call to <code>string.Format<\/code>. This can lead to a lot of allocations &#8211; the boxing of arguments, allocation of an argument array, and of course the resulting string itself. Also, it leaves no wiggle room in the meaning of the actual interpolation.<\/p>\n<p>In C# 10 we&#8217;ve added a library pattern that allows an API to &#8220;take over&#8221; the handling of an interpolated string argument expression. As an example, consider <code>StringBuilder.Append<\/code>:<\/p>\n<pre><code class=\"language-c#\">var sb = new StringBuilder();\r\nsb.Append($\"Hello {args[0]}, how are you?\");<\/code><\/pre>\n<p>Up until now, this would call the <code>Append(string? value)<\/code> overload with a newly allocated and computed string, appending that to the <code>StringBuilder<\/code> in one chunk. However, <code>Append<\/code> now has a new overload <code>Append(ref StringBuilder.AppendInterpolatedStringHandler handler)<\/code> which takes precedence over the string overload when an interpolated string is used as argument.<\/p>\n<p>In general, when you see parameter types of the form <code>SomethingInterpolatedStringHandler<\/code> the API author has done some work behind the scenes to handle interpolated strings more appropriately for their purposes. In the case of our <code>Append<\/code> example, the strings <code>\"Hello \"<\/code>, <code>args[0]<\/code> and <code>\", how are you?\"<\/code> will be individually appended to the <code>StringBuilder<\/code>, which is much more efficient and has the same outcome.<\/p>\n<p>Sometimes you want to do the work of building the string only under certain conditions. An example is <code>Debug.Assert<\/code>:<\/p>\n<pre><code class=\"language-c#\">Debug.Assert(condition, $\"{SomethingExpensiveHappensHere()}\");<\/code><\/pre>\n<p>In most cases, the condition will be true and the second parameter is unused. However, all of the arguments are computed on every call, needlessly slowing down execution. <code>Debug.Assert<\/code> now has an overload with a custom interpolated string builder, which ensures that the second argument isn&#8217;t even evaluated unless the condition is false.<\/p>\n<p>Finally, here&#8217;s an example of actually changing the behavior of string interpolation in a given call: <code>String.Create()<\/code> lets you specify the <code>IFormatProvider<\/code> used to format the expressions in the holes of the interpolated string argument itself:<\/p>\n<pre><code class=\"language-c#\">String.Create(CultureInfo.InvariantCulture, $\"The result is {result}\");<\/code><\/pre>\n<p>You can learn more about interpolated string handlers, <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/tokens\/interpolated#compilation-of-interpolated-strings\">in this article<\/a> and this <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/whats-new\/tutorials\/interpolated-string-handler\">tutorial<\/a> on creating a custom handler.<\/p>\n<h3>Constant interpolated strings<\/h3>\n<p>If all the holes of an interpolated string are constant strings, then the resulting string is now also constant. This lets you use string interpolation syntax in more places, like attributes:<\/p>\n<pre><code class=\"language-c#\">[Obsolete($\"Call {nameof(Discard)} instead\")]<\/code><\/pre>\n<p>Note that the holes must be filled with constant <em>strings<\/em>. Other types, like numeric or date values, cannot be used because they are sensitive to <code>Culture<\/code>, and can&#8217;t be computed at compile time.<\/p>\n<h2>Other improvements<\/h2>\n<p>C# 10 has a number of smaller improvements across the language. Some of these just make C# work in the way you expect.<\/p>\n<h3>Mix declarations and variables in deconstruction<\/h3>\n<p>Prior to C# 10, deconstruction required all variables to be new, or all of them to be previously declared. In C# 10, you can mix:<\/p>\n<pre><code class=\"language-c#\">int x2;\r\nint y2;\r\n(x2, y2) = (0, 1);       \/\/ Works in C# 9\r\n(var x, var y) = (0, 1); \/\/ Works in C# 9\r\n(x2, var y3) = (0, 1);   \/\/ Works in C# 10 onwards<\/code><\/pre>\n<p>Find out more in the article on <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/fundamentals\/functional\/deconstruct\">deconstruction<\/a>.<\/p>\n<h3>Improved definite assignment<\/h3>\n<p>C# produces errors if you use a value that has not been definitely assigned. C# 10 understands your code better and produces less spurious errors. These same improvements also mean you&#8217;ll see less spurious errors and warnings for null references.<\/p>\n<p>Find out more about C# definite assignment in the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/whats-new\/csharp-10#improved-definite-assignment\">what&#8217;s new in C# 10 article<\/a>.<\/p>\n<h3>Extended property patterns<\/h3>\n<p>C# 10 adds extended property patterns to make it easier to access nested property values in patterns. For example, if we add an address to the <code>Person<\/code> record above, we can pattern match in both of the ways shown here:<\/p>\n<pre><code class=\"language-c#\">object obj = new Person\r\n{\r\n    FirstName = \"Kathleen\",\r\n    LastName = \"Dollard\",\r\n    Address = new Address { City = \"Seattle\" }\r\n};\r\n\r\nif (obj is Person { Address: { City: \"Seattle\" } })\r\n    Console.WriteLine(\"Seattle\");\r\n\r\nif (obj is Person { Address.City: \"Seattle\" }) \/\/ Extended property pattern\r\n    Console.WriteLine(\"Seattle\");<\/code><\/pre>\n<p>The extended property pattern simplifies the code and makes it easier to read, particularly when matching against multiple properties.<\/p>\n<p>Find out more about extended property patterns in the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/operators\/patterns#property-pattern\">pattern matching article<\/a>.<\/p>\n<h3>Caller expression attribute<\/h3>\n<p><code>CallerArgumentExpressionAttribute<\/code> supplies information about the context of a method call. Like the other CompilerServices attributes, this attribute is applied to an optional parameter. In this case, a string:<\/p>\n<pre><code class=\"language-c#\">void CheckExpression(bool condition, \r\n    [CallerArgumentExpression(\"condition\")] string? message = null )\r\n{\r\n    Console.WriteLine($\"Condition: {message}\");\r\n}<\/code><\/pre>\n<p>The parameter name passed to <code>CallerArgumentExpression<\/code> is the name of a different parameter. The expression passed as the argument to that parameter will be contained in the string. For example,<\/p>\n<pre><code class=\"language-c#\">var a = 6;\r\nvar b = true;\r\nCheckExpression(true);\r\nCheckExpression(b);\r\nCheckExpression(a &gt; 5);\r\n\r\n\/\/ Output:\r\n\/\/ Condition: true\r\n\/\/ Condition: b\r\n\/\/ Condition: a &gt; 5<\/code><\/pre>\n<p>A good example of how this attribute can be used is ArgumentNullException.ThrowIfNull(). It avoids have to pass in the parameter name by defaulting it from the provided value:<\/p>\n<pre><code class=\"language-C#\">void MyMethod(object value)\r\n{\r\n    ArgumentNullException.ThrowIfNull(value);\r\n}<\/code><\/pre>\n<p>Find out more about <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/language-reference\/attributes\/caller-information#argument-expressions\">CallerArgumentExpressionAttribute<\/a><\/p>\n<h2>Preview features<\/h2>\n<p>C# 10 GA includes <em>static abstract members in interfaces<\/em> as a preview feature. Rolling out a preview feature in GA allows us to get feedback on a feature that will take longer than a single release to create. <em>Static abstract members in interfaces<\/em> is the basis for a new set of generic math constraints that allow you to abstract over which operators are available. You can read more about <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/preview-features-in-net-6-generic-math\/\">generic math constraints in this article<\/a>.<\/p>\n<h2>Closing<\/h2>\n<p>Install .NET 6 or Visual Studio 2022, enjoy C# 10, and tell us what you think!<\/p>\n<ul>\n<li>Kathleen Dollard (PM for the .NET Languages) and Mads Torgersen (C# Lead Designer)<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Check out the great new features in C# 10 as part of .NET 6 and Visual Studio 2022, that make your code prettier, more expressive, and faster.<\/p>\n","protected":false},"author":1097,"featured_media":37391,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756],"tags":[46],"class_list":["post-37390","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","tag-c"],"acf":[],"blog_post_summary":"<p>Check out the great new features in C# 10 as part of .NET 6 and Visual Studio 2022, that make your code prettier, more expressive, and faster.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37390","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=37390"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37390\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/37391"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=37390"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=37390"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=37390"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}