{"id":44774,"date":"2023-03-14T02:00:00","date_gmt":"2023-03-14T10:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=44774"},"modified":"2023-03-15T09:20:23","modified_gmt":"2023-03-15T17:20:23","slug":"announcing-dotnet-8-preview-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-2\/","title":{"rendered":"Announcing .NET 8 Preview 2"},"content":{"rendered":"<p>We&#8217;re excited to share what&#8217;s new in <a href=\"https:\/\/dotnet.microsoft.com\/next\">.NET 8 Preview 2<\/a>. This release is a quick follow-up to the larger <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-1\/\">Preview 1 release<\/a>. You&#8217;ll continue to see many more features show up with these monthly releases. .NET 6 and 7 users will want to follow this release closely. We&#8217;ve focused on making it a straightforward upgrade path.<\/p>\n<p>You can <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/8.0\">download .NET 8 Preview 2<\/a> for Linux, macOS, and Windows.<\/p>\n<ul>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/8.0\">Installers and binaries<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/main\/documentation\/supported-tags.md\">Container images<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/tree\/main\/release-notes\/8.0\">Release notes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/8.0\/known-issues.md\">Known issues<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/issues\">GitHub issue tracker<\/a><\/li>\n<\/ul>\n<p>Check out what&#8217;s new in <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-dotnet-8-preview-2\">ASP.NET Core<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-ef8-preview-2\">EF Core<\/a> in the Preview 2 release. Stay current with what&#8217;s new and coming in <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/whats-new\/dotnet-8\">What&#8217;s New in .NET 8<\/a>. It will be kept updated throughout the release.<\/p>\n<p>.NET 8 has been tested with 17.6 Preview 2. We recommend that you use the <a href=\"https:\/\/visualstudio.com\/preview\">preview channel builds<\/a> if you want to try .NET 8 with the Visual Studio family of products. Visual Studio for Mac support for .NET 8 isn\u2019t yet available.<\/p>\n<p>Let&#8217;s take a look at some new features.<\/p>\n<p><a href=\"https:\/\/dotnet.microsoft.com\/next\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/03\/dotnet-8-preview-2.png\" alt=\"Download .NET 8 Preview 2\" \/><\/a><\/p>\n<h2>Libraries<\/h2>\n<p>The new features in Preview 2 are in the libraries.<\/p>\n<h3>System.ComponentModel.DataAnnotations Extensions<\/h3>\n<p>We&#8217;ve <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82311\">introduced extensions<\/a> to the built-in validation attributes in System.ComponentModel.DataAnnotations.<\/p>\n<h4>RequiredAttribute.DisallowAllDefaultValues<\/h4>\n<p>The <code>RequiredAttribute<\/code> now allows validating that structs do not equal their default values. For example:<\/p>\n<pre><code class=\"language-cs\">[Required(DisallowAllDefaultValues = true)]\npublic Guid MyGuidValue { get; set; }<\/code><\/pre>\n<p>This example will fail validation if its value equals <code>Guid.Empty<\/code>.<\/p>\n<h4>RangeAttribute exclusive bounds<\/h4>\n<p>Users can now specify exclusive bounds in their range validation:<\/p>\n<pre><code class=\"language-cs\">[Range(0d, 1d, MinimumIsExclusive = true, MaximumIsExclusive = true)]\npublic double Sample { get; set; }<\/code><\/pre>\n<p>This attribute accepts any values in the open interval but rejects the boundary values <code>0<\/code> and <code>1<\/code>.<\/p>\n<h4>LengthAttribute<\/h4>\n<p>The <code>LengthAttribute<\/code> can now be used to set both lower and upper bounds for strings or collections:<\/p>\n<pre><code class=\"language-cs\">[Length(10, 20)] \/\/ Require at least 10 elements and at most 20 elements.\npublic ICollection&lt;int&gt; Values { get; set; }<\/code><\/pre>\n<h4>AllowedValuesAttribute and DeniedValuesAttribute<\/h4>\n<p>These attributes can be used to specify allow lists and deny lists for validating a property:<\/p>\n<pre><code class=\"language-cs\">[AllowedValues(\"apple\", \"banana\", \"mango\")]\npublic string Fruit { get; set; }\n\n[DeniedValues(\"pineapple\", \"anchovy\", \"broccoli\")]\npublic string PizzaTopping { get; set; }<\/code><\/pre>\n<h4>Base64StringAttribute<\/h4>\n<p>As the name suggests, this attribute validates that a given string is a valid Base64 representation.<\/p>\n<h3>System.Reflection: introspection support for function pointers<\/h3>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/improvements-in-native-code-interop-in-net-5-0\/\">Function pointers<\/a> were added as part of .NET 5 and C# 9, however, we didn&#8217;t add a matching experience for the feature in Reflection. This new feature adds the <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/69273\">capability to obtain function pointer metadata via Reflection<\/a>, including parameter types, the return type and the calling conventions. Previously, the <code>IntPtr<\/code> type was used for a function pointer type such as with <code>typeof(delegate*&lt;void&gt;())<\/code> or when obtaining a function pointer type through reflection such as with <code>FieldInfo.FieldType<\/code>.<\/p>\n<p>A function pointer instance, which is a physical address to a function, continues to be represented as an <code>IntPtr<\/code>; only the reflection type was changed with this feature.This new functionality is currently implemented in the CoreCLR runtime and in <code>MetadataLoadContext<\/code>. Support for the Mono and NativeAOT runtimes are expected later.<\/p>\n<p>An example using reflection:<\/p>\n<pre><code class=\"language-cs\">FieldInfo fieldInfo = typeof(MyClass).GetField(nameof(MyClass._fp));\n\n\/\/ Obtain the function pointer type from a field. This used to be the 'IntPtr' type, now it's 'Type':\nType fpType = fieldInfo.FieldType;\n\n\/\/ New methods to determine if a type is a function pointer:\nConsole.WriteLine(fpType.IsFunctionPointer); \/\/ True\nConsole.WriteLine(fpType.IsUnmanagedFunctionPointer); \/\/ True\n\n\/\/ New methods to obtain the return and parameter types:\nConsole.WriteLine($\"Return type: {fpType.GetFunctionPointerReturnType()}\"); \/\/ System.Void\n\nforeach (Type parameterType in fpType.GetFunctionPointerParameterTypes())\n{\n    Console.WriteLine($\"Parameter type: {parameterType}\"); \/\/ System.Int32&amp;\n}\n\n\/\/ Access to custom modifiers and calling conventions requires a \"modified type\":\nType modifiedType = fieldInfo.GetModifiedFieldType();\n\n\/\/ A modified type forwards most members to its underlying type which can be obtained with Type.UnderlyingSystemType:\nType normalType = modifiedType.UnderlyingSystemType;\n\n\/\/ New methods to obtain the calling conventions:\nforeach (Type callConv in modifiedType.GetFunctionPointerCallingConventions())\n{\n    Console.WriteLine($\"Calling convention: {callConv}\");\n    \/\/ System.Runtime.CompilerServices.CallConvSuppressGCTransition\n    \/\/ System.Runtime.CompilerServices.CallConvCdecl\n}\n\n\/\/ New methods to obtain the custom modifiers:\nforeach (Type modreq in modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers())\n{\n    Console.WriteLine($\"Required modifier for first parameter: {modreq }\");\n    \/\/ System.Runtime.InteropServices.InAttribute\n}\n\n\/\/ Sample class that contains a function pointer field:\npublic unsafe class MyClass\n{\n    public delegate* unmanaged[Cdecl, SuppressGCTransition]&lt;in int, void&gt; _fp;\n}<\/code><\/pre>\n<p>Parameterized types including generics, pointers, and arrays such as an array of function pointers (e.g. <code>delegate*&lt;void&gt;[]<\/code>) are supported. Thus, the <code>Type.ElementType<\/code> property and the <code>Type.GetGenericArguments()<\/code> method can be used to obtain further types which ultimately may be a function pointer. In addition, a function pointer parameter type is allowed to be another function pointer type.<\/p>\n<h2>Closing<\/h2>\n<p>.NET 8 Preview 2 offers a short, but exciting array of theme updates, new features, and improvements.<\/p>\n<p>We want to extend our <a href=\"https:\/\/dotnet.microsoft.com\/thanks\">sincere thanks to everyone who has contributed to .NET 8 so far<\/a>, whether it was through code contributions, bug reports, or providing feedback. Your contributions have been instrumental in the making .NET 8 Previews, and we look forward to continuing to work together to build a brighter future for .NET and the entire technology community.<\/p>\n<p>What are you waiting for? <a href=\"https:\/\/dotnet.microsoft.com\/next\">Go see for yourself what&#8217;s next in .NET!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 8 Preview 2 contains a few updates\/new features from Preview 1, including introspection support for function pointers, as well as some other runtime and libraries improvements.<\/p>\n","protected":false},"author":114002,"featured_media":44834,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[7701],"class_list":["post-44774","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-dotnet-8"],"acf":[],"blog_post_summary":"<p>.NET 8 Preview 2 contains a few updates\/new features from Preview 1, including introspection support for function pointers, as well as some other runtime and libraries improvements.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/44774","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\/114002"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=44774"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/44774\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/44834"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=44774"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=44774"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=44774"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}