diff --git a/docs/core/compatibility/sdk/8.0/dotnet-restore-audit.md b/docs/core/compatibility/sdk/8.0/dotnet-restore-audit.md index d9ddad501659d..5940d7679d441 100644 --- a/docs/core/compatibility/sdk/8.0/dotnet-restore-audit.md +++ b/docs/core/compatibility/sdk/8.0/dotnet-restore-audit.md @@ -13,7 +13,7 @@ Previously, `dotnet restore` did not emit any security vulnerability warnings by ## New behavior -If you're developing with the .NET 8 SDK or a later version, `dotnet restore` produces security vulnerability warnings by default for *all* restored projects. When you load a solution or project, or run a CI/CD script, this change may break your workflow if you have `` enabled. +If you're developing with the .NET 8 SDK or a later version, `dotnet restore` produces security vulnerability warnings by default for *all* restored projects. When you load a solution or project, or run a CI/CD script, this change might break your workflow if you have `` enabled. ## Version introduced @@ -25,7 +25,7 @@ This change is a [behavioral change](../../categories.md#behavioral-change). ## Reason for change -In most cases when you restore a package, you want to know whether the restored package version contains any known security vulnerabilities. This functionality was added as it is a highly requested feature and security concerns continue to increase each year where known security issues can not be visible enough to taking immediate action. +Many users want to know whether the packages they restore contain any known security vulnerabilities. This functionality was a highly requested feature. Security concerns continue to increase each year and some known security issues aren't visible enough to take immediate action. ## Recommended action @@ -39,4 +39,5 @@ In most cases when you restore a package, you want to know whether the restored ## See also +- [Audit for security vulnerabilities (`dotnet restore`)](../../../tools/dotnet-restore.md#audit-for-security-vulnerabilities) - [Auditing package dependencies for security vulnerabilities](/nuget/concepts/auditing-packages) diff --git a/docs/core/diagnostics/metrics-instrumentation.md b/docs/core/diagnostics/metrics-instrumentation.md index 9f76c3addeaa8..f94352b137928 100644 --- a/docs/core/diagnostics/metrics-instrumentation.md +++ b/docs/core/diagnostics/metrics-instrumentation.md @@ -327,7 +327,7 @@ summarize the distribution differently or offer more configuration options. ```csharp using System; using System.Diagnostics.Metrics; - + class Program { // BEWARE! Static initializers only run when code in a running method refers to a static variable. @@ -336,7 +336,7 @@ summarize the distribution differently or offer more configuration options. static Meter s_meter = new Meter("HatCo.Store"); static ObservableCounter s_coatsSold = s_meter.CreateObservableCounter("hatco.store.coats_sold", () => s_rand.Next(1,10)); static Random s_rand = new Random(); - + static void Main(string[] args) { Console.ReadLine(); @@ -502,8 +502,7 @@ Press p to pause, r to resume, q to quit. ### Best practices -- Although the API allows any object to be used as the tag value, numeric types and strings are anticipated by collection tools. Other types may or may not be - supported by a given collection tool. +- Although the API allows any object to be used as the tag value, numeric types and strings are anticipated by collection tools. Other types may or may not be supported by a given collection tool. - We recommend tag names follow the [OpenTelemetry naming guidelines](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/metrics.md#general-guidelines), which use lowercase dotted hierarchal names with '_' characters to separate multiple words in the same element. If tag names are reused in different metrics or other telemetry diff --git a/docs/core/tools/buildcheck-rules/bc0101.md b/docs/core/tools/buildcheck-rules/bc0101.md new file mode 100644 index 0000000000000..bae5a470c2666 --- /dev/null +++ b/docs/core/tools/buildcheck-rules/bc0101.md @@ -0,0 +1,19 @@ +--- +title: "BC0101: Shared output path" +description: Learn about .NET SDK error BC0101, which occurs when an output path or intermediate outpath is shared between multiple projects. +ms.topic: error-reference +ms.date: 07/10/2024 +f1_keywords: +- BC0101 +--- +# BC0101 + +**This article applies to:** ✔️ .NET 9 SDK and later versions + +It's not recommended to share an output path or intermediate output path between multiple projects. Such practice can lead to silent overwrites of the outputs. Order of write depends on the order of the build, which isn't guaranteed unless explicitly configured. Sharing an output path can cause nondeterministic behavior of the build. + +If you want to produce outputs in a consolidated output folder, consider using the [Artifacts output layout](../../sdk/artifacts-output.md) or [Microsoft.Build.Artifacts SDK](https://github.com/microsoft/MSBuildSdks/tree/main/src/Artifacts). + +The full error message is similar to the following example: + +> **BC0101: Two projects should not share their OutputPath or IntermediateOutputPath locations.** diff --git a/docs/core/tools/buildcheck-rules/bc0102.md b/docs/core/tools/buildcheck-rules/bc0102.md new file mode 100644 index 0000000000000..a389f980c62b6 --- /dev/null +++ b/docs/core/tools/buildcheck-rules/bc0102.md @@ -0,0 +1,21 @@ +--- +title: "BC0102: Double writes" +description: Learn about .NET SDK error BC0102, which occurs when multiple tasks attempt to write to a single file. +ms.topic: error-reference +ms.date: 07/10/2024 +f1_keywords: +- BC0102 +--- +# BC0102 + +**This article applies to:** ✔️ .NET 9 SDK and later versions + +Multiple tasks attempt to write to a single file. Such behavior can lead to nondeterminism of a build (results can be dependent on the order of task execution if they belong to independent projects) or to lost updates. + +If multiple tasks need to produce or update a single file in a one-by-one pipeline fashion, each intermediate output should be given a distinct name. Distinct files prevent silent mixups if any of the tasks in the chain are skipped or removed. + +The full error message is similar to the following example: + +> **BC0102: Two tasks should not write the same file.** + +This rule flags a similar problem as [BC0101 - Shared output path](bc0101.md), but applies more generally to *any* tasks that write to a file. diff --git a/docs/core/tools/buildcheck-rules/index.md b/docs/core/tools/buildcheck-rules/index.md new file mode 100644 index 0000000000000..e59c848cf8a9f --- /dev/null +++ b/docs/core/tools/buildcheck-rules/index.md @@ -0,0 +1,20 @@ +--- +title: BuildCheck rule list +description: A complete list of BCxxxx build check errors. +ms.topic: error-reference +ms.date: 07/10/2024 +ms.custom: updateeachrelease +f1_keywords: +- BC0101 +- BC0102 +--- +# BuildCheck rule list + +**This article applies to:** ✔️ .NET 9 SDK and later versions + +The following list includes all build-check warnings that you might get from the .NET SDK. + +| Rule | Message | +|---------------------|-------------------------------------------------------------------------------------| +| [BC0101](bc0101.md) | Two projects should not share their OutputPath or IntermediateOutputPath locations. | +| [BC0102](bc0102.md) | Two tasks should not write the same file. | diff --git a/docs/core/tools/dotnet-restore.md b/docs/core/tools/dotnet-restore.md index 925257230df91..9d98a8e7f70ba 100644 --- a/docs/core/tools/dotnet-restore.md +++ b/docs/core/tools/dotnet-restore.md @@ -192,9 +192,11 @@ There are three specific settings that `dotnet restore` ignores: ## Audit for security vulnerabilities -Starting in .NET 8, you can opt into NuGet security auditing for `dotnet restore`. This auditing produces a report of security vulnerabilities with the affected package name, the severity of the vulnerability, and a link to the advisory for more details. +Starting in .NET 8, `dotnet restore` includes NuGet security auditing. This auditing produces a report of security vulnerabilities with the affected package name, the severity of the vulnerability, and a link to the advisory for more details. -To opt into security auditing, set the `` MSBuild property to `true` in your project file. Additionally, to retrieve the known vulnerability dataset, ensure that you have the NuGet.org central registry defined as one of your package sources: +To opt out of the security auditing, set the `` MSBuild property to `false` in your project file. + +To retrieve the known vulnerability dataset, ensure that you have the NuGet.org central registry defined as one of your package sources: ```xml @@ -203,3 +205,7 @@ To opt into security auditing, set the `` MSBuild property to `true` ``` You can configure the level at which auditing will fail by setting the `` MSBuild property. Possible values are `low`, `moderate`, `high`, and `critical`. For example if you only want to see moderate, high, and critical advisories, you can set the property to `moderate`. + +Starting in .NET 9, NuGet audits both *direct* and *transitive* package references, by default. In .NET 8, only *direct* package references are audited. You can change the mode by setting the `` MSBuild property to `direct` or `all`. + +For more information, see [Auditing package dependencies for security vulnerabilities](/nuget/concepts/auditing-packages). diff --git a/docs/core/whats-new/dotnet-8/sdk.md b/docs/core/whats-new/dotnet-8/sdk.md index 7a14c52df866a..fa5fc8f18e588 100644 --- a/docs/core/whats-new/dotnet-8/sdk.md +++ b/docs/core/whats-new/dotnet-8/sdk.md @@ -19,6 +19,7 @@ This section contains the following subtopics: - [Simplified output paths](#simplified-output-paths) - ['dotnet workload clean' command](#dotnet-workload-clean-command) - ['dotnet publish' and 'dotnet pack' assets](#dotnet-publish-and-dotnet-pack-assets) +- [`dotnet restore` security auditing](#dotnet-restore-security-auditing) - [Template engine](#template-engine) - [Source Link](#source-link) - [Source-build SDK](#source-build-sdk) diff --git a/docs/core/whats-new/dotnet-9/libraries.md b/docs/core/whats-new/dotnet-9/libraries.md index c28564d453739..a27571ef38352 100644 --- a/docs/core/whats-new/dotnet-9/libraries.md +++ b/docs/core/whats-new/dotnet-9/libraries.md @@ -2,16 +2,47 @@ title: What's new in .NET libraries for .NET 9 description: Learn about the new .NET libraries features introduced in .NET 9. titleSuffix: "" -ms.date: 06/11/2024 +ms.date: 07/11/2024 ms.topic: whats-new --- # What's new in .NET libraries for .NET 9 -This article describes new features in the .NET libraries for .NET 9. It's been updated for .NET 9 Preview 5. +This article describes new features in the .NET libraries for .NET 9. It's been updated for .NET 9 Preview 6. + +## Base64Url + +Base64 is an encoding scheme that translates arbitrary bytes into text composed of a specific set of 64 characters. It's a very common approach for transferring data and has long been supported via a variety of methods, such as with or . However, some of the characters it uses makes it less than ideal for use in some circumstances you might otherwise want to use it, such as in query strings. In particular, the 64 characters that comprise the Base64 table include '+' and '/', both of which have their own meaning in URLs. This led to the creation of the Base64Url scheme, which is similar to Base64 but uses a slightly different set of characters that makes it appropriate for use in URLs contexts. .NET 9 includes the new `Base64Url` class, which provides many helpful and optimized methods for encoding and decoding with `Base64Url` to and from a variety of data types. + +The following example demonstrates using the new class. + +```csharp +ReadOnlySpan bytes = ...; +string encoded = Base64Url.EncodeToString(bytes); +``` ## Collections -The collection type in the namespace includes a new method that you can use to update the priority of an item in the queue. +The collection types in .NET gain the following updates for .NET 9: + +- [Collection lookups with spans](#collection-lookups-with-spans) +- [`OrderedDictionary`](#ordereddictionarytkey-tvalue) +- [PriorityQueue.Remove() method](#priorityqueueremove-method) lets you update the priority of an item in the queue. + +### Collection lookups with spans + +In high-performance code, spans are often used to avoid allocating strings unnecessarily, and lookup tables with types like and are frequently used as caches. However, it's been challenging to use these types together, as there was no safe, built-in mechanism for doing lookups on these types with spans. Now with the new `allows ref struct` feature in C# 13 and new features on these collection types in .NET 9, it's possible to perform these kinds of lookups. + +The following example demonstrates using `Dictionary.GetAlternateLookup` . + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Collections.cs" id="AlternateLookup"::: + +### `OrderedDictionary` + +In many scenarios, you might want to store key-value pairs in a way where order can be maintained (a list of key-value pairs) but where fast lookup by key is also supported (a dictionary of key-value pairs). Since the early days of .NET, the type has supported this scenario, but only in a non-generic manner, with keys and values typed as `object`. .NET 9 introduces the long-requested `OrderedDictionary` collection, which provides an efficient, generic type to support these scenarios. + +The following code uses the new class. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Collections.cs" id="OrderedDictionary"::: ### PriorityQueue.Remove() method @@ -23,15 +54,23 @@ While it's not possible to implement efficient $O(\log n)$ priority updates in t This method unblocks users who want to implement graph algorithms in contexts where asymptotic performance isn't a blocker. (Such contexts include education and prototyping.) For example, here's a [toy implementation of Dijkstra's algorithm](https://github.com/dotnet/runtime/blob/16cb41496d595e2568574cfe11c763d5e05136c9/src/libraries/System.Collections/tests/Generic/PriorityQueue/PriorityQueue.Tests.Dijkstra.cs#L46-L76) that uses the new API. +### `ReadOnlySet` + +It's often desirable to give out read-only views of collections. lets you create a read-only wrapper around an arbitrary mutable , and lets you create a read-only wrapper around an arbitrary mutable . However, past versions of .NET had no built-in support for doing the same with . .NET 9 introduces `ReadOnlySet` to address this. + +The new class enables the following usage pattern. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Collections.cs" id="ReadOnlySet"::: + ## Component model ### `TypeDescriptor` trimming support includes new opt-in trimmer-compatible APIs for describing components. Any application, especially self-contained trimmed applications, can use these new APIs to help support trimming scenarios. -The primary API is the `public static void RegisterType()` method on the `TypeDescriptor` class. This method has the attribute so that the trimmer preserves members for that type. You should call this method once per type, and typically early on. +The primary API is the method on the `TypeDescriptor` class. This method has the attribute so that the trimmer preserves members for that type. You should call this method once per type, and typically early on. -The secondary APIs have a `FromRegisteredType` suffix, such as `TypeDescriptor.GetPropertiesFromRegisteredType(Type componentType)` . Unlike their counterparts that don't have the `FromRegisteredType` suffix, these APIs don't have `[RequiresUnreferencedCode]` or `[DynamicallyAccessedMembers]` trimmer attributes. The lack of trimmer attributes helps consumers by no longer having to either: +The secondary APIs have a `FromRegisteredType` suffix, such as . Unlike their counterparts that don't have the `FromRegisteredType` suffix, these APIs don't have `[RequiresUnreferencedCode]` or `[DynamicallyAccessedMembers]` trimmer attributes. The lack of trimmer attributes helps consumers by no longer having to either: - Suppress trimming warnings, which can be risky. - Propagate a strongly typed `Type` parameter to other methods, which can be cumbersome or infeasible. @@ -87,10 +126,18 @@ The constructor resolution for to other tracing contexts when you [created the `Activity`](xref:System.Diagnostics.ActivitySource.CreateActivity(System.String,System.Diagnostics.ActivityKind,System.Diagnostics.ActivityContext,System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.String,System.Object}},System.Collections.Generic.IEnumerable{System.Diagnostics.ActivityLink},System.Diagnostics.ActivityIdFormat)?displayProperty=nameWithType). New in .NET 9, the `Activity.AddLink(System.Diagnostics.ActivityLink)` API lets you link an `Activity` object to other tracing contexts after it's created. This change aligns with the [OpenTelemetry specifications](https://github.com/open-telemetry/opentelemetry-specification/blob/6360b49d20ae451b28f7ba0be168ed9a799ac9e1/specification/trace/api.md?plain=1#L804) as well. +Previously, you could only link a tracing to other tracing contexts when you [created the `Activity`](xref:System.Diagnostics.ActivitySource.CreateActivity(System.String,System.Diagnostics.ActivityKind,System.Diagnostics.ActivityContext,System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{System.String,System.Object}},System.Collections.Generic.IEnumerable{System.Diagnostics.ActivityLink},System.Diagnostics.ActivityIdFormat)?displayProperty=nameWithType). New in .NET 9, the API lets you link an `Activity` object to other tracing contexts after it's created. This change aligns with the [OpenTelemetry specifications](https://github.com/open-telemetry/opentelemetry-specification/blob/6360b49d20ae451b28f7ba0be168ed9a799ac9e1/specification/trace/api.md?plain=1#L804) as well. :::code language="csharp" source="../snippets/dotnet-9/csharp/Diagnostics.cs" id="AddLink"::: +### Metrics.Gauge instrument + + now provides the `Gauge` instrument according to the OpenTelemetry specification. The `Gauge` instrument is designed to record non-additive values when changes occur. For example, it can measure the background noise level, where summing the values from multiple rooms would be nonsensical. The `Gauge` instrument is a generic type that can record any value type, such as `int`, `double`, or `decimal`. + +The following example demonstrates using the the `Gauge` instrument. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Diagnostics.cs" id="Gauge"::: + ## LINQ New methods and have been introduced. These methods make it possible to aggregate state by key without needing to allocate intermediate groupings via . @@ -107,36 +154,45 @@ New methods and ` overloads](#params-readonlyspant-overloads) +- [`allows ref struct` used in libraries](#allows-ref-struct-used-in-libraries) - [`SearchValues` expansion](#searchvalues-expansion) -### `params ReadOnlySpan` overloads +### `allows ref struct` used in libraries -C# has always supported marking array parameters as [`params`](../../../csharp/language-reference/keywords/method-parameters.md#params-modifier). This keyword enables a simplified calling syntax. For example, the method's second parameter is marked with `params`. You can call this overload with an array or by passing the values individually: +C# 13 introduces the ability to constrain a generic parameter with `allows ref struct`, which tells the compiler and runtime that a `ref struct` can be used for that generic parameter. Many APIs that are compatible with this have now been annotated. For example, the method has an overload that lets you create a `string` by writing directly into its memory, represented as a span. This method has a `TState` argument that's passed from the caller into the delegate that does the actual writing. + +That `TState` type parameter on `String.Create` is now annotated with `allows ref struct`: ```csharp -string result = string.Join(", ", new string[3] { "a", "b", "c" }); -string result = string.Join(", ", "a", "b", "c"); +public static string Create(int length, TState state, SpanAction action) + where TState : allows ref struct; ``` -Prior to .NET 9, when you pass the values individually, the C# compiler emits code identical to the first call by producing an implicit array around the three arguments. - -Starting in C# 13, you can use `params` with any argument that can be constructed via a collection expression, including spans ( and ). That's beneficial for a variety of reasons, including performance. The C# compiler can store the arguments on the stack, wrap a span around them, and pass that off to the method, which avoids the implicit array allocation that would have otherwise resulted. - -.NET 9 now includes over 60 methods with a `params ReadOnlySpan` parameter. Some are brand new overloads, and some are existing methods that already took a `ReadOnlySpan` but now have that parameter marked with `params`. The net effect is if you upgrade to .NET 9 and recompile your code, you'll see performance improvements without making any code changes. That's because the compiler prefers to bind to span-based overloads than to the array-based overloads. +This annotation enables you to pass a span (or any other `ref struct`) as input to this method. -For example, `String.Join` now includes the following overload , which implements the new pattern: +The following example shows a new overload that uses this capability. ```csharp -public static string Join(string? separator, params ReadOnlySpan value) +public static string ToLowerInvariant(ReadOnlySpan input) => + string.Create(span.Length, input, static (stringBuffer, input) => span.ToLowerInvariant(stringBuffer)); ``` -Now, a call like `string.Join(", ", "a", "b", "c")` is made without allocating an array to pass in the `"a"`, `"b"`, and `"c"` arguments. - ### `SearchValues` expansion .NET 8 introduced the type, which provides an optimized solution for searching for specific sets of characters or bytes within spans. In .NET 9, `SearchValues` has been extended to support searching for substrings within a larger string. @@ -147,8 +203,39 @@ The following example searches for multiple animal names within a string value, This new capability has an optimized implementation that takes advantage of the SIMD support in the underlying platform. It also enables higher-level types to be optimized. For example, now utilizes this functionality as part of its implementation. +## Networking + +The networking area includes in the following updates in .NET 9: + +- [SocketsHttpHandler is default in HttpClientFactory](#socketshttphandler-is-default-in-httpclientfactory) +- [System.Net.ServerSentEvents](#systemnetserversentevents) +- [TLS resume with client certificates on Linux](#tls-resume-with-client-certificates-on-linux) + +### SocketsHttpHandler is default in HttpClientFactory + +`HttpClientFactory` creates objects backed by , by default. `HttpClientHandler` is itself backed by , which is much more configurable, including around connection lifetime management. `HttpClientFactory` now uses `SocketsHttpHandler` by default and configures it to set limits on its connection lifetimes to match that of the rotation lifetime specified in the factory. + +### System.Net.ServerSentEvents + +Server-sent events (SSE) is a simple and popular protocol for streaming data from a server to a client. It's used, for example, by OpenAI as part of streaming generated text from its AI services. To simplify the consumption of SSE, the new `System.Net.ServerSentEvents` library provides a parser for easily ingesting server-sent events. + +The following code demonstrates using the new class. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Networking.cs" id="SseParser"::: + +### TLS resume with client certificates on Linux + +*TLS resume* is a feature of the TLS protocol that allows resuming previously established sessions to a server. Doing so avoids a few roundtrips and saves computational resources during TLS handshake. + +*TLS resume* has already been supported on Linux for SslStream connections without client certificates. .NET 9 adds support for TLS resume of mutually authenticated TLS connections, which are common in server-to-server scenarios. The feature is enabled automatically. + ## Reflection +The reflection area includes the following updates for .NET 9: + +- [Persisted assemblies](#persisted-assemblies) +- [Type-name parsing](#type-name-parsing) + ### Persisted assemblies In .NET Core versions and .NET 5-8, support for building an assembly and emitting reflection metadata for dynamically created types was limited to a runnable . The lack of support for *saving* an assembly was often a blocker for customers migrating from .NET Framework to .NET. .NET 9 adds a new type, , that you can use to save an emitted assembly. @@ -161,7 +248,7 @@ The new class includes PD ### Type-name parsing -`TypeName` is a parser for ECMA-335 type names that provides much the same functionality as but is decoupled from the runtime environment. Components like serializers and compilers need to parse and process type names. For example, the Native AOT compiler has switched to using `TypeName` . + is a parser for ECMA-335 type names that provides much the same functionality as but is decoupled from the runtime environment. Components like serializers and compilers need to parse and process type names. For example, the Native AOT compiler has switched to using . The new `TypeName` class provides: @@ -174,7 +261,7 @@ The new `TypeName` class provides: - `IsByRef` and `IsPointer` for working with pointers and managed references. - `GetElementType()` for working with pointers, references, and arrays. - `IsNested` and `DeclaringType` for working with nested types. - - `AssemblyName`, which exposes the assembly name information via the new `AssemblyNameInfo` class. In contrast to `AssemblyName`, the new type is *immutable*, and parsing culture names doesn't create instances of `CultureInfo`. + - `AssemblyName`, which exposes the assembly name information via the new class. In contrast to `AssemblyName`, the new type is *immutable*, and parsing culture names doesn't create instances of `CultureInfo`. Both `TypeName` and `AssemblyNameInfo` types are immutable and don't provide a way to check for equality (they don't implement `IEquatable`). Comparing assembly names is simple, but different scenarios need to compare only a subset of exposed information (`Name`, `Version`, `CultureName`, and `PublicKeyOrToken`). @@ -184,9 +271,52 @@ The following code snippet shows some example usage. The new APIs are available from the [`System.Reflection.Metadata`](https://www.nuget.org/packages/System.Reflection.Metadata/) NuGet package, which can be used with down-level .NET versions. +## Regular expressions + +For regular expressions, .NET 9 includes the following updates: + +- [`[GeneratedRegex]` on properties](#generatedregex-on-properties) +- [`Regex.EnumerateSplits`](#regexenumeratesplits) + +### `[GeneratedRegex]` on properties + +.NET 7 introduced the `Regex` source generator and corresponding attribute. + +The following partial method will be source generated with all the code necessary to implement this `Regex`. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/RegularExpressions.cs" id="GeneratedRegexMethod"::: + +C# 13 supports partial *properties* in addition to partial methods, so starting in .NET 9 you can also use `[GeneratedRegex(...)]` on a property. + +The following partial property is the property equivalent of the previous example. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/RegularExpressions.cs" id="GeneratedRegexProperty"::: + +### `Regex.EnumerateSplits` + +The class provides a method, similar in concept to the method. With `String.Split`, you supply one or more `char` or `string` separators, and the implementation splits the input text on those separators. With `Regex.Split`, instead of specifying the separator as a `char` or `string`, it's specified as a regular expression pattern. + +The following example demonstrates `Regex.Split`. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/RegularExpressions.cs" id="RegexSplit"::: + +However, `Regex.Split` only accepts a `string` as input and doesn't support input being provided as a `ReadOnlySpan`. Also, it outputs the full set of splits as a `string[]`, which requires allocating both the `string` array to hold the results and a `string` for each split. In .NET 9, the new `EnumerateSplits` method enables performing the same operation, but with a span-based input and without incurring any allocation for the results. It accepts a `ReadOnlySpan` and returns an enumerable of objects that represent the results. + +The following example demonstrates `Regex.EnumerateSplits`, taking a `ReadOnlySpan` as input. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/RegularExpressions.cs" id="EnumerateSplits"::: + ## Serialization -In , .NET 9 has new options for serializing JSON and a new singleton that makes it easier to serialize using web defaults. +In , .NET 9 includes the following updates: + +- [Indentation options](#indentation-options) +- [Default web options singleton](#default-web-options-singleton) +- [JsonSchemaExporter](#jsonschemaexporter) +- [Respect nullable annotations](#respect-nullable-annotations) +- [Require non-optional constructor parameters](#require-non-optional-constructor-parameters) +- [Order JsonObject properties](#order-jsonobject-properties) +- [Additional contract metadata APIs](#additional-contract-metadata-apis) ### Indentation options @@ -194,12 +324,262 @@ In , .NET 9 has new options for serializing JSON and a ne :::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Indentation"::: -### Default web options +### Default web options singleton If you want to serialize with the [default options that ASP.NET Core uses](../../../standard/serialization/system-text-json/configure-options.md#web-defaults-for-jsonserializeroptions) for web apps, use the new singleton. :::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Web"::: +### JsonSchemaExporter + +JSON is frequently used to represent types in method signatures as part of remote procedure–calling schemes. It's used, for example, as part of OpenAPI specifications, or as part of tool calling with AI services like those from OpenAI. Developers can serialize and deserialize .NET types as JSON using . But they also need to be able to get a JSON schema that describes the shape of the .NET type (that is, describes the shape of what would be serialized and what can be deserialized). now provides the `JsonSchemaExporter` type, which supports generating a JSON schema that represents a .NET type. + +The following code generates a JSON schema from a type. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Schema"::: + +The type is defined as follows: + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Book"::: + +The generated schema is: + +```json +{ + "type": [ + "object", + "null" + ], + "properties": { + "Title": { + "type": "string" + }, + "Author": { + "type": [ + "string", + "null" + ] + }, + "PublishYear": { + "type": "integer" + } + } +} +``` + +### Respect nullable annotations + + now recognizes nullability annotations of properties and can be configured to enforce those during serialization and deserialization using the `RespectNullableAnnotations` flag. + +The following code shows how to set the option (the `Book` type definition is shown in the previous section): + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="RespectNullable"::: + +You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations` feature switch in your project file (for example, *.csproj* file): + +```xml + + + +``` + +You can configure nullability at an individual property level using the `JsonPropertyInfo.IsGetNullable` and `JsonPropertyInfo.IsSetNullable` properties. + +### Require non-optional constructor parameters + +Historically, has treated non-optional constructor parameters as optional when using constructor-based deserialization. You can change that behavior using the new `RespectRequiredConstructorParameters` flag. + +The following code shows how to set the option: + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="RespectRequired"::: + +The `MyPoco` type is defined as follows: + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Poco"::: + +You can also enable this setting globally using the `System.Text.Json.JsonSerializerOptions.RespectNullableAnnotations` feature switch in your project file (for example, *.csproj* file): + +```xml + + + +``` + +As with earlier versions of , you can configure whether individual properties are required using the property. + +### Order JsonObject properties + +The type now exposes ordered dictionary–like APIs that enable explicit property order manipulation. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="PropertyOrder"::: + +### Additional contract metadata APIs + +The JSON contract API now exposes additional metadata including constructor metadata information and improved attribute provider support for the case of the source generator. + +The new APIs have the following shape: + +```csharp +namespace System.Text.Json.Serialization.Metadata; + +public partial class JsonTypeInfo +{ + // Typically the ConstructorInfo of the active deserialization constructor. + public ICustomAttributeProvider? ConstructorAttributeProvider { get; } +} + +public partial class JsonPropertyInfo +{ + public Type DeclaringType { get; } + // Typically the FieldInfo or PropertyInfo of the property. + public ICustomAttributeProvider? AttributeProvider { get; set; } + // The constructor parameter that has been associated with the current property. + public JsonParameterInfo? AssociatedParameter { get; } +} + +public sealed class JsonParameterInfo +{ + public Type DeclaringType { get; } + public int Position { get; } + public Type ParameterType { get; } + public bool HasDefaultValue { get; } + public object? DefaultValue { get; } + public bool IsNullable { get; } + // Typically the ParameterInfo of the parameter. + public ICustomAttributeProvider? AttributeProvider { get; } +} +``` + +## Spans + +In high-performance code, spans are often used to avoid allocating strings unnecessarily. and continue to revolutionize how code is written in .NET, and every release more and more methods are added that operate on spans. .NET 9 includes the following span-related updates: + +- [File helpers](#file-helpers) +- [`params ReadOnlySpan` overloads](#params-readonlyspant-overloads) + +### File helpers + +The class now has new helpers to easily and directly write `ReadOnlySpan`/`ReadOnlySpan` and `ReadOnlyMemory`/`ReadOnlyMemory` to files. + +The following code efficiently writes a `ReadOnlySpan` to a file. + +```csharp +ReadOnlySpan text = ...; +File.WriteAllText(filePath, text); +``` + +New `ReadOnlySpan.StartsWith` and `ReadOnlySpan.EndsWith` extension methods have also been added for spans, making it easy to test whether a starts or ends with a specific `T` value. + +The following code uses these new convenience APIs. + +:::code language="csharp" source="../snippets/dotnet-9/csharp/Spans.cs" id="StartsWith"::: + +### `params ReadOnlySpan` overloads + +C# has always supported marking array parameters as [`params`](../../../csharp/language-reference/keywords/method-parameters.md#params-modifier). This keyword enables a simplified calling syntax. For example, the method's second parameter is marked with `params`. You can call this overload with an array or by passing the values individually: + +```csharp +string result = string.Join(", ", new string[3] { "a", "b", "c" }); +string result = string.Join(", ", "a", "b", "c"); +``` + +Prior to .NET 9, when you pass the values individually, the C# compiler emits code identical to the first call by producing an implicit array around the three arguments. + +Starting in C# 13, you can use `params` with any argument that can be constructed via a collection expression, including spans ( and ). That's beneficial for usability and performance. The C# compiler can store the arguments on the stack, wrap a span around them, and pass that off to the method, which avoids the implicit array allocation that would have otherwise resulted. + +.NET 9 includes over 60 methods with a `params ReadOnlySpan` parameter. Some are brand new overloads, and some are existing methods that already took a `ReadOnlySpan` but now have that parameter marked with `params`. The net effect is if you upgrade to .NET 9 and recompile your code, you'll see performance improvements without making any code changes. That's because the compiler prefers to bind to span-based overloads than to the array-based overloads. + +For example, `String.Join` now includes the following overload, which implements the new pattern: + +Now, a call like `string.Join(", ", "a", "b", "c")` is made without allocating an array to pass in the `"a"`, `"b"`, and `"c"` arguments. + +## System.Numerics + +The following changes have been made in the namespace: + +- [BigInteger upper limit](#biginteger-upper-limit) +- [`BigMul` APIs](#bigmul-apis) +- [Vector conversion APIs](#vector-conversion-apis) +- [Vector create APIs](#vector-create-apis) +- [Additional acceleration](#additional-acceleration) + +### BigInteger upper limit + + supports representing integer values of essentially arbitrary length. However, in practice, the length is constrained by limits of the underlying computer, such as available memory or how long it would take to compute a given expression. Additionally, there exist some APIs that fail given inputs that result in a value that's too large. Because of these limits, .NET 9 enforces a maximum length of `BigInteger`, which is that it can contain no more than `(2^31) - 1` (approximately 2.14 billion) bits. Such a number represents an almost 256 MB allocation and contains approximately 646.5 million digits. This new limit ensures that all APIs exposed are well behaved and consistent while still allowing numbers that are far beyond most usage scenarios. + +### `BigMul` APIs + +`BigMul` is an operation that produces the full product of two numbers. .NET 9 adds dedicated `BigMul` APIs on `int`, `long`, `uint`, and `ulong` whose return type is the next larger [integer type](../../../csharp/language-reference/builtin-types/integral-numeric-types.md) than the parameter types. + + + +### Vector conversion APIs + +.NET 9 adds dedicated extension APIs for converting between , , , , and . + + + +For same-sized conversions, such as between `Vector4`, `Quaternion`, and `Plane`, these conversions are zero cost. The same can be said for narrowing conversions, such as from `Vector4` to `Vector2` or `Vector3`. For widening conversions, such as from `Vector2` or `Vector3` to `Vector4`, there is the normal API, which initializes new elements to 0, and an `Unsafe` suffixed API that leaves these new elements undefined and therefore can be zero cost. + +### Vector create APIs + +There are new `Create` APIs exposed for , , , and that parity the equivalent APIs exposed for the hardware vector types exposed in the namespace. + + + +These APIs are primarily for convenience and overall consistency across .NET's SIMD-accelerated types. + +### Additional acceleration + +Additional performance improvements have been made to many types in the namespace, including to , , , , , and . + +In some cases, this has resulted in a 2-5x speedup to core APIs including `Matrix4x4` multiplication, creation of from a series of vertices, concatenation, and computing the cross product of a . + +There's also constant folding support for the `SinCos` API, which computes both `Sin(x)` and `Cos(x)` in a single call, making it more efficient. + ## Tensors for AI Tensors are the cornerstone data structure of artificial intelligence (AI). They can often be thought of as multidimensional arrays. @@ -215,7 +595,7 @@ To use the .NET tensor APIs, install the [System.Numerics.Tensors](https://www.n ### New Tensor\ type -The new `Tensor` type expands the AI capabilities of the .NET libraries and runtime. This type: +The new type expands the AI capabilities of the .NET libraries and runtime. This type: - Provides efficient interop with AI libraries like ML.NET, TorchSharp, and ONNX Runtime using zero copies where possible. - Builds on top of for efficient math operations. @@ -242,7 +622,7 @@ The threading APIs include improvements for iterating through tasks, and for pri ### `Task.WhenEach` -A variety of helpful new APIs have been added for working with objects. The new `Task.WhenEach` method lets you iterate through tasks as they complete using an `await foreach` statement. You no longer need to do things like repeatedly call on a set of tasks to pick off the next one that completes. +A variety of helpful new APIs have been added for working with objects. The new method lets you iterate through tasks as they complete using an `await foreach` statement. You no longer need to do things like repeatedly call on a set of tasks to pick off the next one that completes. The following code makes multiple `HttpClient` calls and operates on their results as they complete. @@ -250,7 +630,7 @@ The following code makes multiple `HttpClient` calls and operates on their resul ### Prioritized unbounded channel -The namespace lets you create first-in-first-out (FIFO) channels using the and methods. With FIFO channels, elements are read from the channel in the order they were written to it. In .NET 9, the new `CreateUnboundedPrioritized` method has been added, which orders the elements such that the next element read from the channel is the one deemed to be most important, according to either or a custom . +The namespace lets you create first-in-first-out (FIFO) channels using the and methods. With FIFO channels, elements are read from the channel in the order they were written to it. In .NET 9, the new method has been added, which orders the elements such that the next element read from the channel is the one deemed to be most important, according to either or a custom . The following example uses the new method to create a channel that outputs the numbers 1 through 5 in order, even though they're written to the channel in a different order. diff --git a/docs/core/whats-new/dotnet-9/overview.md b/docs/core/whats-new/dotnet-9/overview.md index 750fbe5d49a04..9ace9e9ac33b0 100644 --- a/docs/core/whats-new/dotnet-9/overview.md +++ b/docs/core/whats-new/dotnet-9/overview.md @@ -2,7 +2,7 @@ title: What's new in .NET 9 description: Learn about the new .NET features introduced in .NET 9. titleSuffix: "" -ms.date: 06/11/2024 +ms.date: 07/10/2024 ms.topic: whats-new --- # What's new in .NET 9 @@ -23,7 +23,13 @@ The runtime also includes numerous performance improvements in the following are - Inlining improvements - PGO improvements: Type checks and casts - Arm64 vectorization in .NET libraries +- Arm64 code generation - Faster exceptions +- Code layout +- Reduced address exposure +- AVX10v1 support +- Hardware intrinsic code generation +- Constant folding for floating point and SIMD operations For more information, see [What's new in the .NET 9 runtime](runtime.md). @@ -47,10 +53,12 @@ For more information, see [What's new in the .NET 9 libraries](libraries.md). The .NET 9 SDK includes improvements to unit testing, including better integration with MSBuild that allows you to run tests in parallel. -For tools, a new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets users (instead of tool authors) decide whether a tool is allowed to run on a newer .NET runtime version than the version the tool targets. +For tools, a new option for [`dotnet tool install`](../../tools/dotnet-tool-install.md) lets users (instead of tool authors) decide whether a tool is allowed to run on a newer .NET runtime version than the version the tool targets. And NuGet security audits run on both direct and transitive package references, by default. The terminal logger is now enabled by default and also has improved usability. For example, the total count of failures and warnings is now summarized at the end of a build. +Finally, new MSBuild script analyzers are available. + For more information, see [What's new in the SDK for .NET 9](sdk.md). ## ML.NET diff --git a/docs/core/whats-new/dotnet-9/runtime.md b/docs/core/whats-new/dotnet-9/runtime.md index 31cd129f01fb4..08c6fd919287c 100644 --- a/docs/core/whats-new/dotnet-9/runtime.md +++ b/docs/core/whats-new/dotnet-9/runtime.md @@ -2,12 +2,12 @@ title: What's new in .NET 9 runtime description: Learn about the new .NET features introduced in the .NET 9 runtime. titleSuffix: "" -ms.date: 06/11/2024 +ms.date: 07/11/2024 ms.topic: whats-new --- # What's new in the .NET 9 runtime -This article describes new features and performance improvements in the .NET runtime for .NET 9. It's been updated for .NET 9 Preview 5. +This article describes new features and performance improvements in the .NET runtime for .NET 9. It's been updated for .NET 9 Preview 6. ## Attribute model for feature switches with trimming support @@ -68,11 +68,24 @@ The following performance improvements have been made for .NET 9: - [Inlining improvements](#inlining-improvements) - [PGO improvements: Type checks and casts](#pgo-improvements-type-checks-and-casts) - [Arm64 vectorization in .NET libraries](#arm64-vectorization-in-net-libraries) +- [Arm64 code generation](#arm64-code-generation) - [Faster exceptions](#faster-exceptions) +- [Code layout](#code-layout) +- [Reduced address exposure](#reduced-address-exposure) +- [AVX10v1 support](#avx10v1-support) +- [Hardware intrinsic code generation](#hardware-intrinsic-code-generation) +- [Constant folding for floating point and SIMD operations](#constant-folding-for-floating-point-and-simd-operations) ### Loop optimizations -Improving code generation for loops is a priority for .NET 9, and the 64-bit compiler features a new optimization called *induction variable (IV) widening*. +Improving code generation for loops is a priority for .NET 9. The following improvements are now available: + +- [Induction variable widening](#induction-variable-widening) +- [Loop counter variable direction](#loop-counter-variable-direction) + +#### Induction variable widening + +The 64-bit compiler features a new optimization called *induction variable (IV) widening*. An IV is a variable whose value changes as the containing loop iterates. In the following `for` loop, `i` is an IV: `for (int i = 0; i < 10; i++)`. If the compiler can analyze how an IV's value evolves over its loop's iterations, it can produce more performant code for related expressions. @@ -93,6 +106,32 @@ static int Sum(int[] arr) The index variable, `i`, is 4 bytes in size. At the assembly level, 64-bit registers are typically used to hold array indices on x64, and in previous .NET versions, the compiler generated code that zero-extended `i` to 8 bytes for the array access, but continued to treat `i` as a 4-byte integer elsewhere. However, extending `i` to 8 bytes requires an additional instruction on x64. With IV widening, the 64-bit JIT compiler now widens `i` to 8 bytes throughout the loop, omitting the zero extension. Looping over arrays is very common, and the benefits of this instruction removal quickly add up. +#### Loop counter variable direction + +The JIT compiler now recognizes when the direction of a loop's counter variable can be flipped without affecting the program's behavior, and does the transformation. + +In the idiomatic `for (int i = ...)` pattern, the counter variable typically increases. Consider the following example: + +```csharp +for (int i = 0; i < 100; i++) +{ + Foo(); +} +``` + +However, on many architectures, it's more performant to decrement the loop's counter, like so: + +``` csharp +for (int i = 100; i > 0; i--) +{ + Foo(); +} +``` + +For the first example, the compiler needs to emit an instruction to increment `i`, followed by an instruction to perform the `i < 100` comparison, followed by a conditional jump to continue the loop if the condition is still `true`—that's three instructions in total. However, if the counter's direction is flipped, one less instruction is needed. For example, on x64, the compiler can use the `dec` instruction to decrement `i`; when `i` reaches zero, the `dec` instruction sets a CPU flag that can be used as the condition for a jump instruction immediately following the `dec`. + +The code size reduction is small, but if the loop runs for a nontrivial number of iterations, the performance improvement can be significant. + ### Inlining improvements One of .NET's goals for the JIT compiler's inliner is to remove as many restrictions that block a method from being inlined as possible. .NET 9 enables inlining of: @@ -140,6 +179,30 @@ Determining the type of an object requires a call into the runtime, which comes A new `EncodeToUtf8` implementation takes advantage of the JIT compiler's ability to emit multi-register load/store instructions on Arm64. This behavior allows programs to process larger chunks of data with fewer instructions. .NET apps across various domains should see throughput improvements on Arm64 hardware that supports these features. Some [benchmarks](https://github.com/dotnet/perf-autofiling-issues/issues/27114) cut their execution time by more than half. +### Arm64 code generation + +The JIT compiler already has the ability to transform its representation of contiguous loads to use the `ldp` instruction (for loading values) on Arm64. .NET 9 extends this ability to *store* operations. + +The `str` instruction stores data from a single register to memory, while the `stp` instruction stores data from a *pair* of registers. Using `stp` instead of `str` means the same task can be accomplished with fewer store operations, which improves execution time. Shaving off one instruction might seem like a small improvement, but if the code runs in a loop for a nontrivial number of iterations, the performance gains can add up quickly. + +For example, consider the following snippet: + +```csharp +class Body { public double x, y, z, vx, vy, vz, mass; } + +static void Advance(double dt, Body[] bodies) +{ + foreach (Body b in bodies) + { + b.x += dt * b.vx; + b.y += dt * b.vy; + b.z += dt * b.vz; + } +} +``` + +The values of `b.x`, `b.y`, and `b.z` are updated in the loop body. At the assembly level, each member could be stored with a `str` instruction; or using `stp`, two of the stores (`b.x` and `b.y`, or `b.y` and `b.z`, because these pairs are contiguous in memory) can be handled with one instruction. To use the `stp` instruction to store to `b.x` and `b.y` simultaneously, the compiler also needs to determine that the computations `b.x + (dt * b.vx)` and `b.y + (dt * b.vy)` are independent of one another and can be performed before storing to `b.x` and `b.y`. + ### Faster exceptions The CoreCLR runtime has adopted a new exception handling approach that improves the performance of exception handling. The new implementation is based on the NativeAOT runtime's exception-handling model. The change removes support for Windows structured exception handling (SEH) and its emulation on Unix. The new approach is supported in all environment except for Windows x86 (32-bit). @@ -155,3 +218,55 @@ The new implementation is enabled by default. However, should you need to switch - Set `System.Runtime.LegacyExceptionHandling` to `true` in the [`runtimeconfig.json` file](../../runtime-config/index.md#runtimeconfigjson). - Set the `DOTNET_LegacyExceptionHandling` environment variable to `1`. + +### Code layout + +Compilers typically reason about a program's control flow using basic *blocks*, where each block is a chunk of code that can only be entered at the first instruction and exited via the last instruction. The order of basic blocks is important. If a block ends with a branch instruction, control flow transfers to another block. One goal of block reordering is to reduce the number of branch instructions in the generated code by maximizing *fall-through* behavior. If each basic block is followed by its most-likely successor, it can "fall into" its successor without needing a jump. + +Until recently, the block reordering in the JIT compiler was limited by the flowgraph implementation. In .NET 9, the JIT compiler's block reordering algorithm has been replaced with a simpler, more global approach. The flowgraph data structures have been refactored to: + +- Remove some restrictions around block ordering. +- Ingrain execution likelihoods into every control-flow change between blocks. + +In addition, profile data is propagated and maintained as the method's flowgraph is transformed. + +### Reduced address exposure + +In .NET 9, the JIT compiler can better track the usage of local variable addresses and avoid unnecessary *address exposure*. + +When the address of a local variable is used, the JIT compiler must take extra precautions when optimizing the method. For example, suppose the compiler is optimizing a method that passes the address of a local variable in a call to another method. Since the callee might use the address to access the local variable, to maintain correctness, the compiler avoids transforming the variable. Addressed-exposed locals can significantly inhibit the compiler's optimization potential. + +### AVX10v1 support + +New APIs have been added for AVX10, which is a new SIMD instruction set from Intel. You can accelerate your .NET applications on AVX10-enabled hardware with vectorized operations using the new `System.Runtime.Intrinsics.X86.Avx10v1` APIs. + +### Hardware intrinsic code generation + +Many hardware intrinsic APIs expect users to pass constant values for certain parameters. These constants are encoded directly into the intrinsic's underlying instruction, rather than being loaded into registers or accessed from memory. If a constant isn't provided, the intrinsic is replaced with a call to a fallback implementation that's functionally equivalent, but slower. + +Consider the following example: + +```csharp +static byte Test1() +{ + Vector128 v = Vector128.Zero; + byte size = 1; + v = Sse2.ShiftRightLogical128BitLane(v, size); + return Sse41.Extract(v, 0); +} +``` + +The use of `size` in the call to `Sse2.ShiftRightLogical128BitLane` can be substituted with the constant 1, and under normal circumstances, the JIT compiler is already capable of this substitution optimization. But when determining whether to generate the accelerated or fallback code for `Sse2.ShiftRightLogical128BitLane`, the compiler detects that a variable is being passed instead of a constant and prematurely decides against "intrinsifying" the call. Starting in .NET 9, the compiler recognizes more cases like this and substitutes the variable argument with its constant value, thus generating the accelerated code. + +### Constant folding for floating point and SIMD operations + +*Constant folding* is an existing optimization in the JIT compiler. *Constant folding* refers to the replacement of expressions that can be computed at compile time with the constants they evaluate to, thus eliminating computations at run time. .NET 9 adds new constant-folding capabilities: + +- For floating-point binary operations, where one of the operands is a constant: + - `x + NaN` is now folded into `NaN`. + - `x * 1.0` is now folded into `x`. + - `x + -0` is now folded into `x`. +- For hardware intrinsics. For example, assuming `x` is a `Vector`: + - `x + Vector.Zero` is now folded into `x`. + - `x & Vector.Zero` is now folded into `Vector.Zero`. + - `x & Vector.AllBitsSet` is now folded into `x`. diff --git a/docs/core/whats-new/dotnet-9/sdk.md b/docs/core/whats-new/dotnet-9/sdk.md index df66b11212336..9c501215e3745 100644 --- a/docs/core/whats-new/dotnet-9/sdk.md +++ b/docs/core/whats-new/dotnet-9/sdk.md @@ -2,12 +2,12 @@ title: What's new in the SDK for .NET 9 description: Learn about the new .NET SDK features introduced in .NET 9. titleSuffix: "" -ms.date: 06/11/2024 +ms.date: 07/10/2024 ms.topic: whats-new --- # What's new in the SDK for .NET 9 -This article describes new features in the .NET SDK for .NET 9. It's been updated for .NET 9 Preview 5. +This article describes new features in the .NET SDK for .NET 9. It's been updated for .NET 9 Preview 6. ## Unit testing @@ -109,3 +109,20 @@ Build succeeded with 3 warning(s) in 0.8s The message lines of the warning no longer have the repeated project and location information that clutter the display. In addition, the build summary shows how many warnings (and errors, if there are any) were generated during the build. If you have feedback about the terminal logger, you can provide it in the [MSBuild repository](https://github.com/dotnet/msbuild/issues). + +## NuGet security audits + +Starting in .NET 8, `dotnet restore` [audits NuGet package references for known vulnerabilities](../../tools/dotnet-restore.md#audit-for-security-vulnerabilities). In .NET 9, the default mode has changed from auditing only *direct* package references to auditing both *direct* and *transitive* package references. + +## MSBuild script analyzers ("BuildChecks") + +.NET 9 introduces a feature that helps guard against defects and regressions in your build scripts. To run the build-check analyzers, add the `/analyze` flag to any command that invokes MSBuild. For example, `dotnet build myapp.sln /analyze` builds the `myapp` solution and runs all configured build checks. + +The following two BuildCheck rules are run: + +- [BC0101](../../tools/buildcheck-rules/bc0101.md) +- [BC0102](../../tools/buildcheck-rules/bc0102.md) + +When a problem is detected, a diagnostic is produced in the build output for the project that contains the issue. + +For more information, see the [design documentation](https://github.com/dotnet/msbuild/blob/main/documentation/specs/proposed/BuildCheck.md). diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs index 36d07c52c98f6..8e292101837ea 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Collections.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text.RegularExpressions; internal static class Collections { @@ -15,4 +18,61 @@ TPriority priority queue.Enqueue(element, priority); } // + + public static void RunIt() + { + // + OrderedDictionary d = new() + { + ["a"] = 1, + ["b"] = 2, + ["c"] = 3, + }; + + d.Add("d", 4); + d.RemoveAt(0); + d.RemoveAt(2); + d.Insert(0, "e", 5); + + foreach (KeyValuePair entry in d) + { + Console.WriteLine(entry); + } + + // Output: + // [e, 5] + // [b, 2] + // [c, 3] + + // + } +} + +internal partial class ReadOnlyCollections +{ + // + private readonly HashSet _set = []; + private ReadOnlySet? _setWrapper; + + public ReadOnlySet Set => _setWrapper ??= new(_set); + // + + // + private readonly Dictionary _wordCounts = new(StringComparer.OrdinalIgnoreCase); + + private static Dictionary CountWords(ReadOnlySpan input) + { + Dictionary wordCounts = new(StringComparer.OrdinalIgnoreCase); + Dictionary.AlternateLookup> spanLookup = + wordCounts.GetAlternateLookup>(); + + foreach (Range wordRange in Regex.EnumerateSplits(input, @"\b\w+\b")) + { + ReadOnlySpan word = input[wordRange]; + spanLookup[word] = spanLookup.TryGetValue(word, out int count) ? count + 1 : 1; + } + + return wordCounts; + } + // } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs index d0f20107533da..af780d855e3b2 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Diagnostics.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.Metrics; internal class Diagnostics { @@ -11,5 +12,15 @@ public static void RunIt() Activity activity = new("LinkTest"); activity.AddLink(activityLink); // + + // + Meter meter = new("MeasurementLibrary.Sound"); + Gauge gauge = meter.CreateGauge( + name: "NoiseLevel", + unit: "dB", // Decibels. + description: "Background Noise Level" + ); + gauge.Record(10, new TagList() { { "Room1", "dB" } } ); + // } } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs new file mode 100644 index 0000000000000..1e242e48baa65 --- /dev/null +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Networking.cs @@ -0,0 +1,17 @@ +using System; +using System.IO; +using System.Net.ServerSentEvents; + +internal class Networking +{ + public async static void RunIt() + { + // + Stream responseStream = new MemoryStream(); + await foreach (SseItem e in SseParser.Create(responseStream).EnumerateAsync()) + { + Console.WriteLine(e.Data); + } + // + } +} diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs index c21c6dd549016..36e7048304053 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Program.cs @@ -1,4 +1,8 @@ //Linq.RunIt(); //Serialization.RunIt(); +//Serialization.RunIt2(); +//Serialization.RunIt3(); //TimeSpan.RunIt(); -Channels.RunIt(); +//Channels.RunIt(); +//RegularExpressions.RunIt(); +Collections.RunIt(); diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj b/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj index 830d1fcca4c2c..ac1ab131ad26e 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Project.csproj @@ -10,6 +10,7 @@ + diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/RegularExpressions.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/RegularExpressions.cs new file mode 100644 index 0000000000000..075edae74761c --- /dev/null +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/RegularExpressions.cs @@ -0,0 +1,45 @@ +using System; +using System.Text.RegularExpressions; + +internal partial class RegularExpressions +{ + // + [GeneratedRegex(@"\b\w{5}\b")] + private static partial Regex FiveCharWord(); + // + + // + [GeneratedRegex(@"\b\w{5}\b")] + private static partial Regex FiveCharWordProperty { get; } + // + + public static void RunIt() + { + // + foreach (string s in Regex.Split("Hello, world! How are you?", "[aeiou]")) + { + Console.WriteLine($"Split: \"{s}\""); + } + + // Output, split by all English vowels: + // Split: "H" + // Split: "ll" + // Split: ", w" + // Split: "rld! H" + // Split: "w " + // Split: "r" + // Split: " y" + // Split: "" + // Split: "?" + + // + + // + ReadOnlySpan input = "Hello, world! How are you?"; + foreach (Range r in Regex.EnumerateSplits(input, "[aeiou]")) + { + Console.WriteLine($"Split: \"{input[r]}\""); + } + // + } +} diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs index 768900c28f072..a9a411760588b 100644 --- a/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Serialization.cs @@ -1,5 +1,8 @@ using System; +using System.Collections.Generic; using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Schema; internal class Serialization { @@ -31,5 +34,74 @@ public static void RunIt() Console.WriteLine(webJson); // {"someValue":42} // + + // + Console.WriteLine(JsonSchemaExporter.GetJsonSchemaAsNode(JsonSerializerOptions.Default, typeof(Book))); + // + + // + JsonObject jObj = new() + { + ["key1"] = true, + ["key3"] = 3 + }; + + Console.WriteLine(jObj is IList>); // True. + + // Insert a new key-value pair at the correct position. + int key3Pos = jObj.IndexOf("key3") is int i and >= 0 ? i : 0; + jObj.Insert(key3Pos, "key2", "two"); + + foreach (KeyValuePair item in jObj) + { + Console.WriteLine($"{item.Key}: {item.Value}"); + } + + // Output: + // key1: true + // key2: two + // key3: 3 + // + } + + public static void RunIt2() + { + // + JsonSerializerOptions options = new() { RespectNullableAnnotations = true }; + + // Throws exception: System.Text.Json.JsonException: The property or field + // 'Title' on type 'Serialization+Book' doesn't allow getting null values. + // Consider updating its nullability annotation. + JsonSerializer.Serialize(new Book { Title = null! }, options); + + // Throws exception: System.Text.Json.JsonException: The property or field + // 'Title' on type 'Serialization+Book' doesn't allow setting null values. + // Consider updating its nullability annotation. + JsonSerializer.Deserialize("""{ "Title" : null }""", options); + // } + + public static void RunIt3() + { + // + JsonSerializerOptions options = new() { RespectRequiredConstructorParameters = true }; + + // Throws exception: System.Text.Json.JsonException: JSON deserialization + // for type 'Serialization+MyPoco' was missing required properties including: 'Value'. + JsonSerializer.Deserialize("""{}""", options); + // + } + + // + public class Book + { + public string Title { get; set; } + public string? Author { get; set; } + public int PublishYear { get; set; } + } + // + + // + record MyPoco(string Value); + // } diff --git a/docs/core/whats-new/snippets/dotnet-9/csharp/Spans.cs b/docs/core/whats-new/snippets/dotnet-9/csharp/Spans.cs new file mode 100644 index 0000000000000..f88281d87cd70 --- /dev/null +++ b/docs/core/whats-new/snippets/dotnet-9/csharp/Spans.cs @@ -0,0 +1,12 @@ +using System; + +internal class Spans +{ + public static bool RunIt() + { + // + ReadOnlySpan text = "some arbitrary text"; + return text.StartsWith('"') && text.EndsWith('"'); // false + // + } +} diff --git a/docs/navigate/tools-diagnostics/toc.yml b/docs/navigate/tools-diagnostics/toc.yml index eef18ea90581e..89d2141dd6b96 100644 --- a/docs/navigate/tools-diagnostics/toc.yml +++ b/docs/navigate/tools-diagnostics/toc.yml @@ -21,66 +21,76 @@ items: href: ../../core/sdk/artifacts-output.md - name: Error messages items: - - name: List of all SDK errors - href: ../../core/tools/sdk-errors/index.md - - name: NETSDK1004 - href: ../../core/tools/sdk-errors/netsdk1004.md - - name: NETSDK1005 and NETSDK1047 - href: ../../core/tools/sdk-errors/netsdk1005.md - - name: NETSDK1013 - href: ../../core/tools/sdk-errors/netsdk1013.md - - name: NETSDK1022 - href: ../../core/tools/sdk-errors/netsdk1022.md - - name: NETSDK1045 - href: ../../core/tools/sdk-errors/netsdk1045.md - - name: NETSDK1059 - href: ../../core/tools/sdk-errors/netsdk1059.md - - name: NETSDK1064 - href: ../../core/tools/sdk-errors/netsdk1064.md - - name: NETSDK1071 - href: ../../core/tools/sdk-errors/netsdk1071.md - - name: NETSDK1073 - href: ../../core/tools/sdk-errors/netsdk1073.md - - name: NETSDK1079 - href: ../../core/tools/sdk-errors/netsdk1079.md - - name: NETSDK1080 - href: ../../core/tools/sdk-errors/netsdk1080.md - - name: NETSDK1082 - href: ../../core/tools/sdk-errors/netsdk1082.md - - name: NETSDK1083 - href: ../../core/tools/sdk-errors/netsdk1083.md - - name: NETSDK1100 - href: ../../core/tools/sdk-errors/netsdk1100.md - - name: NETSDK1112 - href: ../../core/tools/sdk-errors/netsdk1112.md - - name: NETSDK1130 - href: ../../core/tools/sdk-errors/netsdk1130.md - - name: NETSDK1135 - href: ../../core/tools/sdk-errors/netsdk1135.md - - name: NETSDK1136 - href: ../../core/tools/sdk-errors/netsdk1136.md - - name: NETSDK1137 - href: ../../core/tools/sdk-errors/netsdk1137.md - - name: NETSDK1138 - href: ../../core/tools/sdk-errors/netsdk1138.md - - name: NETSDK1141 - href: ../../core/tools/sdk-errors/netsdk1141.md - - name: NETSDK1145 - href: ../../core/tools/sdk-errors/netsdk1145.md - - name: NETSDK1147 - href: ../../core/tools/sdk-errors/netsdk1147.md - - name: NETSDK1149 - href: ../../core/tools/sdk-errors/netsdk1149.md - - name: NETSDK1174 - href: ../../core/tools/sdk-errors/netsdk1174.md - - name: NETSDK1182 - href: ../../core/tools/sdk-errors/netsdk1182.md - - name: NETSDK1195 - href: ../../core/tools/sdk-errors/netsdk1195.md - - name: NETSDK1202 - href: ../../core/tools/sdk-errors/netsdk1202.md - - name: NETSDK1206 - href: ../../core/tools/sdk-errors/netsdk1206.md + - name: NETSDK errors + items: + - name: List of NETSDK errors + href: ../../core/tools/sdk-errors/index.md + - name: NETSDK1004 + href: ../../core/tools/sdk-errors/netsdk1004.md + - name: NETSDK1005 and NETSDK1047 + href: ../../core/tools/sdk-errors/netsdk1005.md + - name: NETSDK1013 + href: ../../core/tools/sdk-errors/netsdk1013.md + - name: NETSDK1022 + href: ../../core/tools/sdk-errors/netsdk1022.md + - name: NETSDK1045 + href: ../../core/tools/sdk-errors/netsdk1045.md + - name: NETSDK1059 + href: ../../core/tools/sdk-errors/netsdk1059.md + - name: NETSDK1064 + href: ../../core/tools/sdk-errors/netsdk1064.md + - name: NETSDK1071 + href: ../../core/tools/sdk-errors/netsdk1071.md + - name: NETSDK1073 + href: ../../core/tools/sdk-errors/netsdk1073.md + - name: NETSDK1079 + href: ../../core/tools/sdk-errors/netsdk1079.md + - name: NETSDK1080 + href: ../../core/tools/sdk-errors/netsdk1080.md + - name: NETSDK1082 + href: ../../core/tools/sdk-errors/netsdk1082.md + - name: NETSDK1083 + href: ../../core/tools/sdk-errors/netsdk1083.md + - name: NETSDK1100 + href: ../../core/tools/sdk-errors/netsdk1100.md + - name: NETSDK1112 + href: ../../core/tools/sdk-errors/netsdk1112.md + - name: NETSDK1130 + href: ../../core/tools/sdk-errors/netsdk1130.md + - name: NETSDK1135 + href: ../../core/tools/sdk-errors/netsdk1135.md + - name: NETSDK1136 + href: ../../core/tools/sdk-errors/netsdk1136.md + - name: NETSDK1137 + href: ../../core/tools/sdk-errors/netsdk1137.md + - name: NETSDK1138 + href: ../../core/tools/sdk-errors/netsdk1138.md + - name: NETSDK1141 + href: ../../core/tools/sdk-errors/netsdk1141.md + - name: NETSDK1145 + href: ../../core/tools/sdk-errors/netsdk1145.md + - name: NETSDK1147 + href: ../../core/tools/sdk-errors/netsdk1147.md + - name: NETSDK1149 + href: ../../core/tools/sdk-errors/netsdk1149.md + - name: NETSDK1174 + href: ../../core/tools/sdk-errors/netsdk1174.md + - name: NETSDK1182 + href: ../../core/tools/sdk-errors/netsdk1182.md + - name: NETSDK1195 + href: ../../core/tools/sdk-errors/netsdk1195.md + - name: NETSDK1202 + href: ../../core/tools/sdk-errors/netsdk1202.md + - name: NETSDK1206 + href: ../../core/tools/sdk-errors/netsdk1206.md + - name: BuildCheck rules + items: + - name: Index of rules + href: ../../core/tools/buildcheck-rules/index.md + - name: BC0101 + href: ../../core/tools/buildcheck-rules/bc0101.md + - name: BC0102 + href: ../../core/tools/buildcheck-rules/bc0102.md - name: .NET CLI items: - name: Overview diff --git a/docs/standard/base-types/best-practices-regex.md b/docs/standard/base-types/best-practices-regex.md index 676f232c5f6a8..3f8c794ac994a 100644 --- a/docs/standard/base-types/best-practices-regex.md +++ b/docs/standard/base-types/best-practices-regex.md @@ -112,7 +112,7 @@ Regular expression patterns that aren't bound to the regular expression engine t Regular expression patterns that are bound to the regular expression engine through the specification of the option are *compiled*. Therefore, when a regular expression object is instantiated, or when a static regular expression method is called and the regular expression can't be found in the cache, the regular expression engine converts the regular expression to an intermediary set of operation codes. These codes are then converted to CIL. When a method is called, the JIT compiler executes the CIL. In contrast to interpreted regular expressions, compiled regular expressions increase startup time but execute individual pattern-matching methods faster. As a result, the performance benefit that results from compiling the regular expression increases in proportion to the number of regular expression methods called. -Regular expression patterns that are bound to the regular expression engine through the adornment of a `RegEx`-returning method with the attribute are *source generated*. The source generator, which plugs into the compiler, emits as C# code a custom `Regex`-derived implementation with logic similar to what `RegexOptions.Compiled` emits in CIL. You get all the throughput performance benefits of `RegexOptions.Compiled` (more, in fact) and the start-up benefits of `Regex.CompileToAssembly`, but without the complexity of `CompileToAssembly`. The source that's emitted is part of your project, which means it's also easily viewable and debuggable. +Regular expression patterns that are bound to the regular expression engine through the adornment of a `Regex`-returning method with the attribute are *source generated*. The source generator, which plugs into the compiler, emits as C# code a custom `Regex`-derived implementation with logic similar to what `RegexOptions.Compiled` emits in CIL. You get all the throughput performance benefits of `RegexOptions.Compiled` (more, in fact) and the start-up benefits of `Regex.CompileToAssembly`, but without the complexity of `CompileToAssembly`. The source that's emitted is part of your project, which means it's also easily viewable and debuggable. To summarize, we recommend that you: diff --git a/docs/standard/memory-and-spans/index.md b/docs/standard/memory-and-spans/index.md index 72387b8cb080c..9c5e8148153df 100644 --- a/docs/standard/memory-and-spans/index.md +++ b/docs/standard/memory-and-spans/index.md @@ -1,18 +1,20 @@ --- -description: "Learn more about: Memory- and span-related types" -title: "Memory and spans" -ms.date: "10/03/2018" +description: "Learn more about: Memory-related and span-related types" +title: "Memory-related and span types" +ms.date: 07/12/2024 helpviewer_keywords: - "Memory" - "Span" - buffers" - "pipeline processing" --- -# Memory- and span-related types +# Memory-related and span types -Starting with .NET Core 2.1, .NET includes a number of interrelated types that represent a contiguous, strongly typed region of arbitrary memory. These include: +.NET includes a number of interrelated types that represent a contiguous, strongly typed region of arbitrary memory. These types are designed to allow the creation of algorithms that *avoid copying memory or allocating on the managed heap* more than necessary. Creating them (either via `Slice`, `AsSpan()`, a collection expression, or their constructors) does not involve duplicating the underlying buffers: only the relevant references and offsets, which represent the "view" of the wrapped memory, are updated. In high-performance code, spans are often used to avoid allocating strings unnecessarily. -- , a type that is used to access a contiguous region of memory. A instance can be backed by an array of type `T`, a buffer allocated with [stackalloc](../../csharp/language-reference/operators/stackalloc.md), or a pointer to unmanaged memory. Because it has to be allocated on the stack, it has a number of restrictions. For example, a field in a class cannot be of type , nor can span be used in asynchronous operations. +The types include: + +- , a type that's used to access a contiguous region of memory. A instance can be backed by an array of type `T`, a buffer allocated with [stackalloc](../../csharp/language-reference/operators/stackalloc.md), or a pointer to unmanaged memory. Because it has to be allocated on the stack, it has a number of restrictions. For example, a field in a class cannot be of type , nor can span be used in asynchronous operations. - , an immutable version of the structure. Instances can be also backed by a . @@ -30,16 +32,11 @@ Starting with .NET Core 2.1, .NET includes a number of interrelated types that r - , a collection of extension methods for converting strings, arrays, and array segments to blocks. -, , and their readonly counterparts are designed to allow the creation of algorithms that avoid copying memory or allocating on the managed heap more than necessary. Creating them (either via `Slice` or their constructors) does not involve duplicating the underlying buffers: only the relevant references and offsets, which represent the "view" of the wrapped memory, are updated. - -> [!NOTE] -> For earlier frameworks, and are available in the [System.Memory NuGet package](https://www.nuget.org/packages/System.Memory/). - For more information, see the namespace. ## Working with memory and span -Because the memory- and span-related types are typically used to store data in a processing pipeline, it is important that developers follow a set of best practices when using , , and related types. These best practices are documented in [Memory\ and Span\ usage guidelines](memory-t-usage-guidelines.md). +Because the memory-related and span-related types are typically used to store data in a processing pipeline, it's important that you follow a set of best practices when using , , and related types. These best practices are documented in [Memory\ and Span\ usage guidelines](memory-t-usage-guidelines.md). ## See also diff --git a/docs/visual-basic/programming-guide/concepts/linq/how-to-combine-linq-queries-with-regular-expressions.md b/docs/visual-basic/programming-guide/concepts/linq/how-to-combine-linq-queries-with-regular-expressions.md index e346f8ba5a123..a9dbf46038abd 100644 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-combine-linq-queries-with-regular-expressions.md +++ b/docs/visual-basic/programming-guide/concepts/linq/how-to-combine-linq-queries-with-regular-expressions.md @@ -77,7 +77,7 @@ Class LinqRegExVB End Class ``` -Note that you can also query the object that is returned by a `RegEx` search. In this example only the value of each match is produced in the results. However, it is also possible to use LINQ to perform all kinds of filtering, sorting, and grouping on that collection. Because is a non-generic collection, you have to explicitly state the type of the range variable in the query. +Note that you can also query the object that is returned by a `Regex` search. In this example only the value of each match is produced in the results. However, it is also possible to use LINQ to perform all kinds of filtering, sorting, and grouping on that collection. Because is a non-generic collection, you have to explicitly state the type of the range variable in the query. ## Compile the code diff --git a/docs/visual-basic/programming-guide/concepts/linq/how-to-query-the-contents-of-files-in-a-folder-linq.md b/docs/visual-basic/programming-guide/concepts/linq/how-to-query-the-contents-of-files-in-a-folder-linq.md index 723b52e4c1594..d7cbf42722745 100644 --- a/docs/visual-basic/programming-guide/concepts/linq/how-to-query-the-contents-of-files-in-a-folder-linq.md +++ b/docs/visual-basic/programming-guide/concepts/linq/how-to-query-the-contents-of-files-in-a-folder-linq.md @@ -6,19 +6,19 @@ ms.assetid: edacbcd3-f3e4-4429-a8be-28a58dc0dd70 --- # How to query the contents of files in a folder (LINQ) (Visual Basic) -This example shows how to query over all the files in a specified directory tree, open each file, and inspect its contents. This type of technique could be used to create indexes or reverse indexes of the contents of a directory tree. A simple string search is performed in this example. However, more complex types of pattern matching can be performed with a regular expression. For more information, see [How to: Combine LINQ Queries with Regular Expressions (Visual Basic)](how-to-combine-linq-queries-with-regular-expressions.md). - -## Example - +This example shows how to query over all the files in a specified directory tree, open each file, and inspect its contents. This type of technique could be used to create indexes or reverse indexes of the contents of a directory tree. A simple string search is performed in this example. However, more complex types of pattern matching can be performed with a regular expression. For more information, see [How to: Combine LINQ Queries with Regular Expressions (Visual Basic)](how-to-combine-linq-queries-with-regular-expressions.md). + +## Example + ```vb Imports System.IO -Module Module1 - 'QueryContents - Public Sub Main() - - ' Modify this path as necessary. - Dim startFolder = "C:\Program Files (x86)\Microsoft Visual Studio 14.0" +Module Module1 + 'QueryContents + Public Sub Main() + + ' Modify this path as necessary. + Dim startFolder = "C:\Program Files (x86)\Microsoft Visual Studio 14.0" ' Take a snapshot of the folder contents. Dim dir As New DirectoryInfo(startFolder) @@ -27,7 +27,7 @@ Module Module1 Dim searchTerm = "Welcome" ' Search the contents of each file. - ' A regular expression created with the RegEx class + ' A regular expression created with the Regex class ' could be used instead of the Contains method. Dim queryMatchingFiles = From file In fileList _ Where file.Extension = ".html" _