头图

Today we released .NET 7 Preview 5. This preview release of .NET 7 includes improvements to general math to make it easier for API authors, a new ML.NET text classification API that adds state-of-the-art deep learning techniques, for natural language processing, to source Various improvements to the code generator and a new Roslyn analyzer and fixer for RegexGenerator, and several performance improvements in CodeGen, observability, JSON serialization/deserialization, and using streams.
You can download .NET 7 Preview 5 for Windows, macOS and Linux.

.NET 7 Preview 5 has been tested with Visual Studio 17.3 Preview 2. If you want to use .NET 7 with the Visual Studio family of products, we recommend using the Preview Channel version . If you are using macOS, we recommend using the latest Visual Studio 2022 for Mac preview . Now, let's take a look at some of the latest updates in this release.

observability

The goal of observability is to help you better understand the state of your application as scale and technical complexity increase.

▌ Expose efficient ActivityEvent and ActivityLink tag enumerator methods

#68056

The exposed methods can be used to enumerate Tag objects in performance critical scenarios without any additional allocation and fast item access.

 var tags = new List<KeyValuePair<string, object?>>()
{
    new KeyValuePair<string, object?>("tag1", "value1"),
    new KeyValuePair<string, object?>("tag2", "value2"),
};

ActivityLink link = new ActivityLink(default, new ActivityTagsCollection(tags));

foreach (ref readonly KeyValuePair<string, object?> tag in link.EnumerateTagObjects())
{
    // Consume the link tags without any extra allocations or value copying.
}            

ActivityEvent e = new ActivityEvent("SomeEvent", tags: new ActivityTagsCollection(tags));

foreach (ref readonly KeyValuePair<string, object?> tag in e.EnumerateTagObjects())
{
    // Consume the event's tags without any extra allocations or value copying.
}

Observability:

 https://devblogs.microsoft.com/dotnet/opentelemetry-net-reaches-v1-0/?ocid=AID3042760

System.Text.Json polymorphism

#63747
System.Text.Json now supports serialization and deserialization of polymorphic type hierarchies using property annotations:

 [JsonDerivedType(typeof(Derived))]
public class Base
{
    public int X { get; set; }
}

public class Derived : Base
{
    public int Y { get; set; }
}

This configuration enables polymorphic serialization for Base, especially when the runtime type is Derived:

 Base value = new Derived();
JsonSerializer.Serialize<Base>(value); // { "X" : 0, "Y" : 0 }

Note that this will not enable polymorphic deserialization, as the payload will be round-tripped as a Base:

 Base value = JsonSerializer.Deserialize<Base>(@"{ ""X"" : 0, ""Y"" : 0 }");
value is Derived; // false

▌Using a type discriminator To enable polymorphic deserialization, the user needs to specify a type discriminator for the derived class:

 [JsonDerivedType(typeof(Base), typeDiscriminator: "base")]
[JsonDerivedType(typeof(Derived), typeDiscriminator: "derived")]
public class Base
{
    public int X { get; set; }
}

public class Derived : Base
{
    public int Y { get; set; }
}

JSON will now be emitted along with the type discriminator metadata:

 Base value = new Derived();
JsonSerializer.Serialize<Base>(value); // { "$type" : "derived", "X" : 0, "Y" : 0 }

Can be used to polymorphically deserialize values:

 Base value = JsonSerializer.Deserialize<Base>(@"{ ""$type"" : ""derived"", ""X"" : 0, ""Y"" : 0 }");
value is Derived; // true

Type discriminator identifiers can also be integers, so the following forms are valid:

 [JsonDerivedType(typeof(Derived1), 0)]
[JsonDerivedType(typeof(Derived2), 1)]
[JsonDerivedType(typeof(Derived3), 2)]
public class Base { }

JsonSerializer.Serialize<Base>(new Derived2()); // { "$type" : 1, ... }
#63747:
https://github.com/dotnet/runtime/issues/63747

▌Utf8JsonReader.CopyString

#54410
Until today, Utf8JsonReader.GetString() has been the only way for users to work with decoded JSON strings. This will always allocate a new string, which may not be suitable for some performance sensitive applications. The newly included CopyString method allows unescaped UTF-8 or UTF-16 strings to be copied to a user-owned buffer:

 int valueLength = reader.HasReadOnlySequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
char[] buffer = ArrayPool<char>.Shared.Rent(valueLength);
int charsRead = reader.CopyString(buffer);
ReadOnlySpan<char> source = buffer.Slice(0, charsRead);

ParseUnescapedString(source); // handle the unescaped JSON string
ArrayPool<char>.Shared.Return(buffer);

Or if dealing with UTF-8 is preferable:

 ReadOnlySpan<byte> source = stackalloc byte[0];
if (!reader.HasReadOnlySequence && !reader.ValueIsEscaped)
{
    source = reader.ValueSpan; // No need to copy to an intermediate buffer if value is span without escape sequences
}
else
{
    int valueLength = reader.HasReadOnlySequence ? checked((int)ValueSequence.Length) : ValueSpan.Length;
    Span<byte> buffer = valueLength <= 256 ? stackalloc byte[256] : new byte[valueLength];
    int bytesRead = reader.CopyString(buffer);
    source = buffer.Slice(0, bytesRead);
}

ParseUnescapedBytes(source);

▌Source generation improvements

Added source code generation support for IAsyncEnumerable<T> ( #59268 ), JsonDocument ( #59954 ) and DateOnly/TimeOnly ( #53539 ) types.
E.g:

 [JsonSerializable(typeof(typeof(MyPoco))]
public class MyContext : JsonSerializerContext {}

public class MyPoco
{
    // Use of IAsyncEnumerable that previously resulted 
    // in JsonSerializer.Serialize() throwing NotSupportedException 
    public IAsyncEnumerable<int> Data { get; set; } 
}

// It now works and no longer throws NotSupportedException
JsonSerializer.Serialize(new MyPoco { Data = ... }, MyContext.MyPoco);

System.IO.Stream , ReadExactly and ReadAtLeast

#16598

One of the most common mistakes when using Stream.Read() is that Read() may return less data than is available in the Stream, and less data than the incoming buffer. Even for programmers who are aware of this, it's annoying to write the same loop every time they want to read from the Stream.

To address this situation, we have added new methods to the System.IO.Stream base class:

 namespace System.IO;

public partial class Stream
{
    public void ReadExactly(Span<byte> buffer);
    public void ReadExactly(byte[] buffer, int offset, int count);

    public ValueTask ReadExactlyAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
    public ValueTask ReadExactlyAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default);

    public int ReadAtLeast(Span<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true);
    public ValueTask<int> ReadAtLeastAsync(Memory<byte> buffer, int minimumBytes, bool throwOnEndOfStream = true, CancellationToken cancellationToken = default);
}

The new ReadExactly method guarantees that the requested number of bytes are read exactly. Throws EndOfStreamException if the stream ends before the requested bytes are read.

 using FileStream f = File.Open("readme.md");
byte[] buffer = new byte[100];

f.ReadExactly(buffer); // guaranteed to read 100 bytes from the file

The new ReadAtLeast method will read at least the requested number of bytes. If more data is available, it can read more data up to the size of the buffer. If the stream ends before the requested bytes are read, an EndOfStreamException is thrown (in advanced cases, when you want the benefits of ReadAtLest but you also want to handle end-of-stream scenarios yourself, you can choose not to throw an exception).

 using FileStream f = File.Open("readme.md");
byte[] buffer = new byte[100];

int bytesRead = f.ReadAtLeast(buffer, 10);
// 10 <= bytesRead <= 100

New Roslyn analyzer and fixer for RegexGenerator

#69872

In Regular Expression Improvements in .NET 7, Stephen Toub describes the new RegexGenerator source generator , which allows you to generate regular expressions statically at compile time for better performance. To take advantage of this, first you have to find where in your code you can use it, and then make changes to each of them. This sounds like a perfect job for the Roslyn analyzer and fixer, so we've added one in Preview 5.

▌Analyzer

A new analyzer is included in .NET 7 that converts searches to Regex uses using the RegexGenerator source generator. The analyzer will detect the use of Regex constructors, and the use of Regex static methods that satisfy the following conditions:

The supplied parameter has a known value at compile time. The output of the source code generator depends on these values, so they must be known at compile time.

They are part of an application targeting .NET 7. The new analyzer is included in the .NET 7 target package, and only applications targeting .NET 7 are eligible to use this analyzer.

LangVersion ( learn more ) is higher than 10. Currently the regular expression source generator requires LangVersion to be set to preview.

Here is the new analyzer running in Visual Studio:

▌Code Fixer

A code fix is also included in .NET 7, which does two things. First, it suggests using the RegexGenerator source generator method, and gives you the option to override the default name. Then it replaces the original code with a call to the new method.

Here are the new code fixes in action in Visual Studio:

general mathematics

In .NET 6, we previewed a feature called Generic Math that allows .NET developers to leverage static APIs, including operators, in generic code. This feature will directly benefit API authors who can simplify their codebase. Other developers will benefit indirectly as the APIs they use will start supporting more types without requiring explicit support for every numeric type.

In .NET 7, we've made improvements to the implementation and responded to community feedback. For more information on changes and available APIs, please see our general math specific announcements .

Performance improvements when calling members of System.Reflection

#67917
The overhead of using reflection to call members (whether methods, constructors, or property gettersetters) has been greatly reduced when multiple calls to the same member are made. Typical gain is 3-4 times faster.
Using the BenchmarkDotNet package:

 using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Reflection;

namespace ReflectionBenchmarks
{
    internal class Program
    {
        static void Main(string[] args)
        {
            BenchmarkRunner.Run<InvokeTest>();
        }
    }

    public class InvokeTest
    {
        private MethodInfo? _method;
        private object[] _args = new object[1] { 42 };

        [GlobalSetup]
        public void Setup()
        {
            _method = typeof(InvokeTest).GetMethod(nameof(InvokeMe), BindingFlags.Public | BindingFlags.Static)!;
        }

        [Benchmark]
        // *** This went from ~116ns to ~39ns or 3x (66%) faster.***
        public void InvokeSimpleMethod() => _method!.Invoke(obj: null, new object[] { 42 });

        [Benchmark]
        // *** This went from ~106ns to ~26ns or 4x (75%) faster. ***
        public void InvokeSimpleMethodWithCachedArgs() => _method!.Invoke(obj: null, _args);

        public static int InvokeMe(int i) => i;
    }
}

ML.NET Text Classification API

Text classification is the process of applying labels or categories to text.
Common use cases include:

  • Classify emails as spam or not spam
  • Analyze whether sentiment is positive or negative from customer reviews
  • Apply tags to support tickets

Text classification is a subset of classification, so today you can use existing classification algorithms in ML.NET to solve text classification problems. However, these algorithms do not address text classification and the common challenges of modern deep learning techniques.

We're excited to introduce the ML.NET Text Classification API, which makes it easier to train custom text classification models and brings the latest state-of-the-art deep learning techniques for natural language processing to ML.NET.

See our ML.NET specific announcement for more details.

code generation

Many thanks to community contributors.

@singleaccretion made 23 PR contributions during Preview 5, the highlights are:

Improve redundant branch optimization to handle more side effects #68447

PUTARG_STK/x86: mark push [mem] candidate reg optional #68641

Duplicate propagation on LCL_FLD #68592

@Sandreenko Done allowing StoreLclVar src to be IND/FLD #59315 . @hez2010 Fixed CircleInConvex test in #68475 .

More contributions from @anthonycanino , @aromaa , and @ta264 are included in later sections.

▌Arm64

#68363 Combine "msub" (multiply two register values, subtract product from third register value) and "madd" (multiply two register values, add third register value) logic.

Arm64: Made CpBlkUnroll and InitBlkUnroll use SIMD registers to initialize copies of memory blocks smaller than 128 bytes (see performance improvement details ).

▌Cycle optimization

#67930 More scenarios dealing with loop clones now support looping backwards or forwards in increments > 1 (see performance improvement details).

#68588 Improve null checking for 'this' object Moves null checking to objects outside of the loop (see performance improvement details ).

x86/x64 optimization

General optimization

  • PR #68105 enables multiple nested "no GC" zone requests.
  • PR #69034 removes the "raise parameter" tail call restriction.

Modern JIT

As the community increases contributions to the JIT codebase, it becomes important to restructure and modernize our codebase to allow our contributors to easily grow and rapidly develop code.
In Preview 5, we've done a lot of work internally to clean up the JIT's intermediate representation and remove the constraints imposed by past design decisions. In many cases, this work results in less memory usage and higher throughput for the JIT itself, while in other cases it results in better code quality. Here are some highlights:

  • Remove CLS_VAR #68524
  • Remove GT_ARGPLACE #68140
  • Remove GT_PUTARG_TYPE #68748
    The above allows us to remove the old limitation in JIT inlining when inlining functions with arguments of type byte/sbyte/short/ushort, thus improving code quality (allows inlining to replace small arguments #69068 )

One area for improvement is better understanding of unsafe code that involves reading and writing structs and struct fields. @SingleAccretion has made a huge difference in this area by converting the JIT's internal model to a more general "physical" model. This paves the way for the JIT to better reason about unsafe code using features like struct reinterpretation:

  • Physical Value #68712
  • Implement constant folding for VNF_BitCast #68979

There are other small cleanups to simplify JIT IR:

  • Remove GTF_LATE_ARG #68617
  • Replace GT_RET_EXPR in inline candidate arguments #69117
  • Remove store as operand of call in LIR #68460

Enable library pruning

As we've described before, trimming lets the SDK remove unused code from your self-contained applications to make them smaller. However, pruning warnings may indicate that the application is not compatible with pruning. For applications to be compatible, all their references must also be compatible.

For this, we need the library to also employ pruning . In Preview 5, we've worked to make it easier to find and fix pruning warnings in libraries using the Roslyn analyzer. To see trim warnings for a library, add <IsTrimmable>true</IsTrimmable> to your project file. After fixing the warning, applications trimmed with your library will be smaller and trim compatible. See Preparing .NET Libraries for Trimming - .NET | Microsoft Docs to learn more about library trimming.

Targeting .NET 7

To target .NET 7, you need to use the .NET 7 Target Framework Moniker (TFM) in your project file. E.g:

 <TargetFramework>net7.0</TargetFramework>

Full suite of .NET 7 TFMs, including operation-specific TFMs.

  • net7.0
  • net7.0-Android
  • net7.0-ios
  • net7.0-maccatalyst
  • net7.0-macos
  • net7.0-tvos
  • net7.0-windows
    We hope that upgrading from .NET 6 to .NET 7 should be simple. Please report any breaking changes you find while testing your existing applications with .NET 7.

support

.NET 7 is a Short Term Support (STS) release, which means it will receive free support and patches for 18 months from the release date. It's important to note that all versions are of the same quality. The only difference is the length of the supports. For more information on the .NET support policy, see the .NET and .NET Core Official Support Policy .
We recently changed the "Current" name to "Short Term Support (STS)". We are rolling out this change .

major changes

You can find the latest list of .NET 7 breaking changes by reading the Breaking Changes in .NET 7 document. It lists breaking changes by region and version, with links to detailed descriptions.

To see what breaking changes have been proposed but still under review, follow the Proposed .NET Breaking Changes GitHub issue .

route map

.NET releases include products, libraries, runtimes, and tools, and represent collaboration between multiple teams inside and outside Microsoft. You can learn more about these areas by reading the product roadmap:

We thank you for all your support and contributions to .NET. Please try .NET 7 Preview 5 and let us know what you think!


Long press to identify the QR code and follow Microsoft China MSDN

Check out .NET 7 Preview 5


微软技术栈
423 声望995 粉丝

微软技术生态官方平台。予力众生,成就不凡!微软致力于用技术改变世界,助力企业实现数字化转型。