We’re excited to share what’s new in .NET 8 Preview 2. This release is a quick follow-up to the larger Preview 1 release. You’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’ve focused on making it a straightforward upgrade path.
You can download .NET 8 Preview 2 for Linux, macOS, and Windows.
Check out what’s new in ASP.NET Core and EF Core in the Preview 2 release. Stay current with what’s new and coming in What’s New in .NET 8. It will be kept updated throughout the release.
.NET 8 has been tested with 17.6 Preview 2. We recommend that you use the preview channel builds if you want to try .NET 8 with the Visual Studio family of products. Visual Studio for Mac support for .NET 8 isn’t yet available.
Let’s take a look at some new features.
Libraries
The new features in Preview 2 are in the libraries.
System.ComponentModel.DataAnnotations Extensions
We’ve introduced extensions to the built-in validation attributes in System.ComponentModel.DataAnnotations.
RequiredAttribute.DisallowAllDefaultValues
The RequiredAttribute
now allows validating that structs do not equal their default values. For example:
[Required(DisallowAllDefaultValues = true)]
public Guid MyGuidValue { get; set; }
This example will fail validation if its value equals Guid.Empty
.
RangeAttribute exclusive bounds
Users can now specify exclusive bounds in their range validation:
[Range(0d, 1d, MinimumIsExclusive = true, MaximumIsExclusive = true)]
public double Sample { get; set; }
This attribute accepts any values in the open interval but rejects the boundary values 0
and 1
.
LengthAttribute
The LengthAttribute
can now be used to set both lower and upper bounds for strings or collections:
[Length(10, 20)] // Require at least 10 elements and at most 20 elements.
public ICollection<int> Values { get; set; }
AllowedValuesAttribute and DeniedValuesAttribute
These attributes can be used to specify allow lists and deny lists for validating a property:
[AllowedValues("apple", "banana", "mango")]
public string Fruit { get; set; }
[DeniedValues("pineapple", "anchovy", "broccoli")]
public string PizzaTopping { get; set; }
Base64StringAttribute
As the name suggests, this attribute validates that a given string is a valid Base64 representation.
System.Reflection: introspection support for function pointers
Function pointers were added as part of .NET 5 and C# 9, however, we didn’t add a matching experience for the feature in Reflection. This new feature adds the capability to obtain function pointer metadata via Reflection, including parameter types, the return type and the calling conventions. Previously, the IntPtr
type was used for a function pointer type such as with typeof(delegate*<void>())
or when obtaining a function pointer type through reflection such as with FieldInfo.FieldType
.
A function pointer instance, which is a physical address to a function, continues to be represented as an IntPtr
; only the reflection type was changed with this feature.This new functionality is currently implemented in the CoreCLR runtime and in MetadataLoadContext
. Support for the Mono and NativeAOT runtimes are expected later.
An example using reflection:
FieldInfo fieldInfo = typeof(MyClass).GetField(nameof(MyClass._fp));
// Obtain the function pointer type from a field. This used to be the 'IntPtr' type, now it's 'Type':
Type fpType = fieldInfo.FieldType;
// New methods to determine if a type is a function pointer:
Console.WriteLine(fpType.IsFunctionPointer); // True
Console.WriteLine(fpType.IsUnmanagedFunctionPointer); // True
// New methods to obtain the return and parameter types:
Console.WriteLine($"Return type: {fpType.GetFunctionPointerReturnType()}"); // System.Void
foreach (Type parameterType in fpType.GetFunctionPointerParameterTypes())
{
Console.WriteLine($"Parameter type: {parameterType}"); // System.Int32&
}
// Access to custom modifiers and calling conventions requires a "modified type":
Type modifiedType = fieldInfo.GetModifiedFieldType();
// A modified type forwards most members to its underlying type which can be obtained with Type.UnderlyingSystemType:
Type normalType = modifiedType.UnderlyingSystemType;
// New methods to obtain the calling conventions:
foreach (Type callConv in modifiedType.GetFunctionPointerCallingConventions())
{
Console.WriteLine($"Calling convention: {callConv}");
// System.Runtime.CompilerServices.CallConvSuppressGCTransition
// System.Runtime.CompilerServices.CallConvCdecl
}
// New methods to obtain the custom modifiers:
foreach (Type modreq in modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers())
{
Console.WriteLine($"Required modifier for first parameter: {modreq }");
// System.Runtime.InteropServices.InAttribute
}
// Sample class that contains a function pointer field:
public unsafe class MyClass
{
public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp;
}
Parameterized types including generics, pointers, and arrays such as an array of function pointers (e.g. delegate*<void>[]
) are supported. Thus, the Type.ElementType
property and the Type.GetGenericArguments()
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.
Closing
.NET 8 Preview 2 offers a short, but exciting array of theme updates, new features, and improvements.
We want to extend our sincere thanks to everyone who has contributed to .NET 8 so far, 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.
What are you waiting for? Go see for yourself what’s next in .NET!
What about support for C++ in any version of .NET beyond 4.8??
I have no intention of porting my apps to C# or even starting anything new in C#.
I totally agree Jerry. What is the Managed C++ roadmap if any?
Myself and others agree with Andrew T. The Bound properties on the Range Attribute need to be renamed. “Exclusive” is ambiguous and will no doubt get misused. “Included” would be clearer. Since these comments will undoubtedly be closed soon, there is more description and discussion is happening over here https://github.com/dotnet/core/issues/8134#issuecomment-1483911888
what about the
URLBase64
? and Stream base Base64/UrlBase64 ?You have a bug on the code example for the AllowedValuesAttribute and DeniedValuesAttribute feature. In the code example, you erroneously have “pineapple” with the DeniedValues. This is a glaring, critical, show-stopping bug. Please fix.
Too many versions of .NET. Can I safely target the ones that are released today? That is not clear to me. Plus, I still can't use VS 2022 because WPF is a second class citizen, and you made sure I can't use VS 2019 to target later versions of .NET. I need to write first class LOB applications for corporate customers. WPF has been the best way to do that for desktop applications on Windows,...
You can stick with .NET Framework 4.8 in your WPF app, it’s supported as long as Windows 10 and 11 are supported. If you find problems with WPF in .NET 6, 7 or 8, you can report it in GitHub: https://github.com/dotnet/wpf
That repo might as well not exist.
Just out of interest, how do you mean Jim when you say “That repo might as well not exist”. I think I can see what you are talking about – in that it doesn’t mention anything newer than .Net 7 preview…
It seems that compared to preview1, the .net team with hundreds of people does not seem to have made any significant results this month.
Let’s wait and see, we still have several months ahead until November 2023, when .NET 8 is going to be released.
What is the runtime overhead of [Range] and [Length]?
For the “RangeAttribute exclusive bounds” example, when IsBoundExclusive is false it reads to me like the bound is not exclusive so would be inclusive. Setting IsBoundExclusive to false causing the validation behavior to invalidate the boundary value as input is the opposite of what I would expect. Basically if you set the boundary values as not exclusive, I would expect a closed interval.
Thanks – it’s been fixed in the example (note that the property names changed too).
Primary constructors seem to be missing from this post.