diff --git a/docs/azure/includes/dotnet-all.md b/docs/azure/includes/dotnet-all.md index 238d4bf7772df..b0eb914808678 100644 --- a/docs/azure/includes/dotnet-all.md +++ b/docs/azure/includes/dotnet-all.md @@ -112,7 +112,7 @@ | Synapse - Monitoring | NuGet [1.0.0-beta.3](https://www.nuget.org/packages/Azure.Analytics.Synapse.Monitoring/1.0.0-beta.3) | [docs](/dotnet/api/overview/azure/Analytics.Synapse.Monitoring-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.Synapse.Monitoring_1.0.0-beta.3/sdk/synapse/Azure.Analytics.Synapse.Monitoring/) | | Synapse - Spark | NuGet [1.0.0-preview.8](https://www.nuget.org/packages/Azure.Analytics.Synapse.Spark/1.0.0-preview.8) | [docs](/dotnet/api/overview/azure/Analytics.Synapse.Spark-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-preview.8](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.Synapse.Spark_1.0.0-preview.8/sdk/synapse/Azure.Analytics.Synapse.Spark/) | | System Events | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Messaging.EventGrid.SystemEvents/1.0.0) | [docs](/dotnet/api/overview/azure/Messaging.EventGrid.SystemEvents-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Messaging.EventGrid.SystemEvents_1.0.0/sdk/eventgrid/Azure.Messaging.EventGrid.SystemEvents/) | -| System.ClientModel | NuGet [1.4.2](https://www.nuget.org/packages/System.ClientModel/1.4.2)
NuGet [1.5.0-beta.1](https://www.nuget.org/packages/System.ClientModel/1.5.0-beta.1) | [docs](/dotnet/api/overview/azure/System.ClientModel-readme) | GitHub [1.4.2](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.4.2/sdk/core/System.ClientModel/)
GitHub [1.5.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.5.0-beta.1/sdk/core/System.ClientModel/) | +| System.ClientModel | NuGet [1.5.0](https://www.nuget.org/packages/System.ClientModel/1.5.0) | [docs](/dotnet/api/overview/azure/System.ClientModel-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.5.0/sdk/core/System.ClientModel/) | | Tables | NuGet [12.11.0](https://www.nuget.org/packages/Azure.Data.Tables/12.11.0) | [docs](/dotnet/api/overview/azure/Data.Tables-readme) | GitHub [12.11.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Data.Tables_12.11.0/sdk/tables/Azure.Data.Tables/) | | Text Analytics | NuGet [5.3.0](https://www.nuget.org/packages/Azure.AI.TextAnalytics/5.3.0) | [docs](/dotnet/api/overview/azure/AI.TextAnalytics-readme) | GitHub [5.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.TextAnalytics_5.3.0/sdk/textanalytics/Azure.AI.TextAnalytics/) | | Text Authoring | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.AI.Language.Text.Authoring/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/AI.Language.Text.Authoring-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Language.Text.Authoring_1.0.0-beta.1/sdk/cognitivelanguage/Azure.AI.Language.Text.Authoring/) | diff --git a/docs/azure/includes/dotnet-new.md b/docs/azure/includes/dotnet-new.md index 33148ce000aeb..ef87ddbbfe5b8 100644 --- a/docs/azure/includes/dotnet-new.md +++ b/docs/azure/includes/dotnet-new.md @@ -116,7 +116,7 @@ | Synapse - Monitoring | NuGet [1.0.0-beta.3](https://www.nuget.org/packages/Azure.Analytics.Synapse.Monitoring/1.0.0-beta.3) | [docs](/dotnet/api/overview/azure/Analytics.Synapse.Monitoring-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.3](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.Synapse.Monitoring_1.0.0-beta.3/sdk/synapse/Azure.Analytics.Synapse.Monitoring/) | | Synapse - Spark | NuGet [1.0.0-preview.8](https://www.nuget.org/packages/Azure.Analytics.Synapse.Spark/1.0.0-preview.8) | [docs](/dotnet/api/overview/azure/Analytics.Synapse.Spark-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-preview.8](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Analytics.Synapse.Spark_1.0.0-preview.8/sdk/synapse/Azure.Analytics.Synapse.Spark/) | | System Events | NuGet [1.0.0](https://www.nuget.org/packages/Azure.Messaging.EventGrid.SystemEvents/1.0.0) | [docs](/dotnet/api/overview/azure/Messaging.EventGrid.SystemEvents-readme) | GitHub [1.0.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Messaging.EventGrid.SystemEvents_1.0.0/sdk/eventgrid/Azure.Messaging.EventGrid.SystemEvents/) | -| System.ClientModel | NuGet [1.4.2](https://www.nuget.org/packages/System.ClientModel/1.4.2)
NuGet [1.5.0-beta.1](https://www.nuget.org/packages/System.ClientModel/1.5.0-beta.1) | [docs](/dotnet/api/overview/azure/System.ClientModel-readme) | GitHub [1.4.2](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.4.2/sdk/core/System.ClientModel/)
GitHub [1.5.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.5.0-beta.1/sdk/core/System.ClientModel/) | +| System.ClientModel | NuGet [1.5.0](https://www.nuget.org/packages/System.ClientModel/1.5.0) | [docs](/dotnet/api/overview/azure/System.ClientModel-readme) | GitHub [1.5.0](https://github.com/Azure/azure-sdk-for-net/tree/System.ClientModel_1.5.0/sdk/core/System.ClientModel/) | | Tables | NuGet [12.11.0](https://www.nuget.org/packages/Azure.Data.Tables/12.11.0) | [docs](/dotnet/api/overview/azure/Data.Tables-readme) | GitHub [12.11.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.Data.Tables_12.11.0/sdk/tables/Azure.Data.Tables/) | | Text Analytics | NuGet [5.3.0](https://www.nuget.org/packages/Azure.AI.TextAnalytics/5.3.0) | [docs](/dotnet/api/overview/azure/AI.TextAnalytics-readme) | GitHub [5.3.0](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.TextAnalytics_5.3.0/sdk/textanalytics/Azure.AI.TextAnalytics/) | | Text Authoring | NuGet [1.0.0-beta.1](https://www.nuget.org/packages/Azure.AI.Language.Text.Authoring/1.0.0-beta.1) | [docs](/dotnet/api/overview/azure/AI.Language.Text.Authoring-readme?view=azure-dotnet-preview&preserve-view=true) | GitHub [1.0.0-beta.1](https://github.com/Azure/azure-sdk-for-net/tree/Azure.AI.Language.Text.Authoring_1.0.0-beta.1/sdk/cognitivelanguage/Azure.AI.Language.Text.Authoring/) | diff --git a/docs/core/diagnostics/media/resource-monitoring-output.png b/docs/core/diagnostics/media/resource-monitoring-output.png index 6928605f149ed..680474ec31143 100644 Binary files a/docs/core/diagnostics/media/resource-monitoring-output.png and b/docs/core/diagnostics/media/resource-monitoring-output.png differ diff --git a/docs/core/diagnostics/snippets/resource-monitoring/Program.cs b/docs/core/diagnostics/snippets/resource-monitoring/Program.cs index 98f41a34baf89..3afec76cbcacb 100644 --- a/docs/core/diagnostics/snippets/resource-monitoring/Program.cs +++ b/docs/core/diagnostics/snippets/resource-monitoring/Program.cs @@ -1,20 +1,29 @@ // using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.ResourceMonitoring; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Spectre.Console; -var services = new ServiceCollection() - .AddLogging(static builder => builder.AddConsole()) - .AddResourceMonitoring(); - -var provider = services.BuildServiceProvider(); +var app = Host.CreateDefaultBuilder() + .ConfigureServices(services => + { + services.AddLogging(static builder => builder.AddConsole()) + .AddResourceMonitoring(); + }) + .Build(); -var monitor = provider.GetRequiredService(); +var monitor = app.Services.GetRequiredService(); +await app.StartAsync(); // using var cancellationTokenSource = new CancellationTokenSource(); var token = cancellationTokenSource.Token; +Console.CancelKeyPress += (_, e) => +{ + e.Cancel = true; + cancellationTokenSource.Cancel(); +}; // await StartMonitoringAsync(monitor, token); @@ -60,11 +69,5 @@ await AnsiConsole.Live(table) await Task.Delay(window); } }); - - Console.CancelKeyPress += (_, e) => - { - e.Cancel = true; - cancellationTokenSource.Cancel(); - }; } // diff --git a/docs/core/diagnostics/snippets/resource-monitoring/resource-monitoring.csproj b/docs/core/diagnostics/snippets/resource-monitoring/resource-monitoring.csproj index 55c42513e68f5..1fbe7160f67f0 100644 --- a/docs/core/diagnostics/snippets/resource-monitoring/resource-monitoring.csproj +++ b/docs/core/diagnostics/snippets/resource-monitoring/resource-monitoring.csproj @@ -1,4 +1,4 @@ - + Exe @@ -11,6 +11,7 @@ + diff --git a/docs/core/extensions/httpclient-factory.md b/docs/core/extensions/httpclient-factory.md index 9392c760a0827..f65983929a069 100644 --- a/docs/core/extensions/httpclient-factory.md +++ b/docs/core/extensions/httpclient-factory.md @@ -221,7 +221,7 @@ An is returne :::code source="snippets/http/configurehandler/Program.cs" id="configurehandler"::: -Configuring the `HttClientHandler` lets you specify a proxy for the `HttpClient` instance among various other properties of the handler. For more information, see [Proxy per client](../../fundamentals/networking/http/httpclient.md#configure-an-http-proxy). +Configuring the `HttpClientHandler` lets you specify a proxy for the `HttpClient` instance among various other properties of the handler. For more information, see [Proxy per client](../../fundamentals/networking/http/httpclient.md#configure-an-http-proxy). ### Additional configuration diff --git a/docs/core/testing/unit-testing-csharp-with-nunit.md b/docs/core/testing/unit-testing-csharp-with-nunit.md index 0e0f1f48763f7..6c2e180417de0 100644 --- a/docs/core/testing/unit-testing-csharp-with-nunit.md +++ b/docs/core/testing/unit-testing-csharp-with-nunit.md @@ -86,12 +86,15 @@ The [dotnet new](../tools/dotnet-new.md) command creates a test project that use > [!NOTE] > Prior to .NET 9, the generated code may reference older versions of the NUnit test framework. You may use [dotnet CLI](/nuget/consume-packages/install-use-packages-dotnet-cli) to update the packages. Alternatively, open the *PrimeService.Tests.csproj* file and replace the contents of the package references item group with the code above. -The test project requires other packages to create and run unit tests. The `dotnet new` command in the previous step added the Microsoft test SDK, the NUnit test framework, and the NUnit test adapter. Now, add the `PrimeService` class library as another dependency to the project. Use the [`dotnet reference add`](../tools/dotnet-reference-add.md) command: +The test project requires other packages to create and run unit tests. The `dotnet new` command in the previous step added the Microsoft test SDK, the NUnit test framework, and the NUnit test adapter. Now, add the `PrimeService` class library as another dependency to the project. Use the [`dotnet add reference`](../tools/dotnet-reference-add.md) command: ```dotnetcli -dotnet reference add ../PrimeService/PrimeService.csproj +dotnet add reference ../PrimeService/PrimeService.csproj ``` +> [!NOTE] +> If you're using .NET 10 SDK or later, you can use the "noun first" form: `dotnet reference add ../PrimeService/PrimeService.csproj`. + You can see the entire file in the [samples repository](https://github.com/dotnet/samples/blob/main/core/getting-started/unit-testing-using-nunit/PrimeService.Tests/PrimeService.Tests.csproj) on GitHub. The following outline shows the final solution layout: diff --git a/docs/csharp/asynchronous-programming/index.md b/docs/csharp/asynchronous-programming/index.md index 4879d49130072..be81a52d2bb17 100644 --- a/docs/csharp/asynchronous-programming/index.md +++ b/docs/csharp/asynchronous-programming/index.md @@ -263,6 +263,36 @@ The code completes the asynchronous breakfast tasks in about 15 minutes. The tot The final code is asynchronous. It more accurately reflects how a person might cook breakfast. Compare the final code with the first code sample in the article. The core actions are still clear by reading the code. You can read the final code the same way you read the list of instructions for making a breakfast, as shown at the beginning of the article. The language features for the `async` and `await` keywords provide the translation every person makes to follow the written instructions: Start tasks as you can and don't block while waiting for tasks to complete. +## Async/await vs ContinueWith + +The `async` and `await` keywords provide syntactic simplification over using directly. While `async`/`await` and `ContinueWith` have similar semantics for handling asynchronous operations, the compiler doesn't necessarily translate `await` expressions directly into `ContinueWith` method calls. Instead, the compiler generates optimized state machine code that provides the same logical behavior. This transformation provides significant readability and maintainability benefits, especially when chaining multiple asynchronous operations. + +Consider a scenario where you need to perform multiple sequential asynchronous operations. Here's how the same logic looks when implemented with `ContinueWith` compared to `async`/`await`: + +### Using ContinueWith + +With `ContinueWith`, each step in a sequence of asynchronous operations requires nested continuations: + +:::code language="csharp" source="snippets/index/ContinueWith-comparison/Program.cs" id="ContinueWithExample"::: + +### Using async/await + +The same sequence of operations using `async`/`await` reads much more naturally: + +:::code language="csharp" source="snippets/index/ContinueWith-comparison/Program.cs" id="AsyncAwaitExample"::: + +### Why async/await is preferred + +The `async`/`await` approach offers several advantages: + +- **Readability**: The code reads like synchronous code, making it easier to understand the flow of operations. +- **Maintainability**: Adding or removing steps in the sequence requires minimal code changes. +- **Error handling**: Exception handling with `try`/`catch` blocks works naturally, whereas `ContinueWith` requires careful handling of faulted tasks. +- **Debugging**: The call stack and debugger experience is much better with `async`/`await`. +- **Performance**: The compiler optimizations for `async`/`await` are more sophisticated than manual `ContinueWith` chains. + +The benefit becomes even more apparent as the number of chained operations increases. While a single continuation might be manageable with `ContinueWith`, sequences of 3-4 or more asynchronous operations quickly become difficult to read and maintain. This pattern, known as "monadic do-notation" in functional programming, allows you to compose multiple asynchronous operations in a sequential, readable manner. + ## Next step > [!div class="nextstepaction"] diff --git a/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/ContinueWith-comparison.csproj b/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/ContinueWith-comparison.csproj new file mode 100644 index 0000000000000..aee73149c0567 --- /dev/null +++ b/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/ContinueWith-comparison.csproj @@ -0,0 +1,8 @@ + + + + Exe + net9.0 + + + \ No newline at end of file diff --git a/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/Program.cs b/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/Program.cs new file mode 100644 index 0000000000000..2f12ffd4e5d1a --- /dev/null +++ b/docs/csharp/asynchronous-programming/snippets/index/ContinueWith-comparison/Program.cs @@ -0,0 +1,114 @@ +using System; +using System.Threading.Tasks; + +namespace ContinueWithComparison +{ + class Program + { + static async Task Main(string[] args) + { + Console.WriteLine("=== ContinueWith approach ==="); + await MakeBreakfastWithContinueWith(); + + Console.WriteLine("\n=== async/await approach ==="); + await MakeBreakfastWithAsyncAwait(); + } + + // + // Using ContinueWith - demonstrates the complexity when chaining operations + static Task MakeBreakfastWithContinueWith() + { + return StartCookingEggsAsync() + .ContinueWith(eggsTask => + { + var eggs = eggsTask.Result; + Console.WriteLine("Eggs ready, starting bacon..."); + return StartCookingBaconAsync(); + }) + .Unwrap() + .ContinueWith(baconTask => + { + var bacon = baconTask.Result; + Console.WriteLine("Bacon ready, starting toast..."); + return StartToastingBreadAsync(); + }) + .Unwrap() + .ContinueWith(toastTask => + { + var toast = toastTask.Result; + Console.WriteLine("Toast ready, applying butter..."); + return ApplyButterAsync(toast); + }) + .Unwrap() + .ContinueWith(butteredToastTask => + { + var butteredToast = butteredToastTask.Result; + Console.WriteLine("Butter applied, applying jam..."); + return ApplyJamAsync(butteredToast); + }) + .Unwrap() + .ContinueWith(finalToastTask => + { + var finalToast = finalToastTask.Result; + Console.WriteLine("Breakfast completed with ContinueWith!"); + }); + } + // + + // + // Using async/await - much cleaner and easier to read + static async Task MakeBreakfastWithAsyncAwait() + { + var eggs = await StartCookingEggsAsync(); + Console.WriteLine("Eggs ready, starting bacon..."); + + var bacon = await StartCookingBaconAsync(); + Console.WriteLine("Bacon ready, starting toast..."); + + var toast = await StartToastingBreadAsync(); + Console.WriteLine("Toast ready, applying butter..."); + + var butteredToast = await ApplyButterAsync(toast); + Console.WriteLine("Butter applied, applying jam..."); + + var finalToast = await ApplyJamAsync(butteredToast); + Console.WriteLine("Breakfast completed with async/await!"); + } + // + + static async Task StartCookingEggsAsync() + { + Console.WriteLine("Starting to cook eggs..."); + await Task.Delay(1000); + return new { Item = "Eggs" }; + } + + static async Task StartCookingBaconAsync() + { + Console.WriteLine("Starting to cook bacon..."); + await Task.Delay(1000); + return new { Item = "Bacon" }; + } + + static async Task StartToastingBreadAsync() + { + Console.WriteLine("Starting to toast bread..."); + await Task.Delay(1000); + return new { Item = "Toast" }; + } + + static async Task ApplyButterAsync(object toast) + { + Console.WriteLine("Applying butter..."); + await Task.Delay(500); + return new { Item = "Buttered Toast" }; + } + + static async Task ApplyJamAsync(object butteredToast) + { + Console.WriteLine("Applying jam..."); + await Task.Delay(500); + return new { Item = "Completed Toast" }; + } + } +} \ No newline at end of file diff --git a/docs/csharp/language-reference/builtin-types/integral-numeric-types.md b/docs/csharp/language-reference/builtin-types/integral-numeric-types.md index e47aadcb812ea..3a6d33c148519 100644 --- a/docs/csharp/language-reference/builtin-types/integral-numeric-types.md +++ b/docs/csharp/language-reference/builtin-types/integral-numeric-types.md @@ -136,7 +136,7 @@ Native sized integer types have special behavior because the storage is determin :::code language="csharp" source="snippets/shared/NativeIntegerTypes.cs" id="MinMax"::: -- You can use constant values in the following ranges: +- While the full range of `nint` and `nuint` may be larger, compile-time constants are restricted to a 32-bit range: - For `nint`: to . - For `nuint`: to . - The compiler provides implicit and explicit conversions to other numeric types. For more information, see [Built-in numeric conversions](numeric-conversions.md). diff --git a/docs/csharp/language-reference/builtin-types/nullable-reference-types.md b/docs/csharp/language-reference/builtin-types/nullable-reference-types.md index 6f81d1f703e80..b32e023fa9ef9 100644 --- a/docs/csharp/language-reference/builtin-types/nullable-reference-types.md +++ b/docs/csharp/language-reference/builtin-types/nullable-reference-types.md @@ -46,6 +46,9 @@ try The examples in the previous section illustrate the nature of nullable reference types. Nullable reference types aren't new class types, but rather annotations on existing reference types. The compiler uses those annotations to help you find potential null reference errors in your code. There's no runtime difference between a non-nullable reference type and a nullable reference type. The compiler doesn't add any runtime checking for non-nullable reference types. The benefits are in the compile-time analysis. The compiler generates warnings that help you find and fix potential null errors in your code. You declare your intent, and the compiler warns you when your code violates that intent. +> [!IMPORTANT] +> Nullable reference annotations don't introduce behavior changes, but other libraries may use reflection to produce different runtime behavior for nullable and non-nullable reference types. Notably, Entity Framework Core reads nullable attributes. It interprets a nullable reference as an optional value, and a non-nullable reference as a required value. + In a nullable enabled context, the compiler performs static analysis on variables of any reference type, both nullable and non-nullable. The compiler tracks the *null-state* of each reference variable as either *not-null* or *maybe-null*. The default state of a non-nullable reference is *not-null*. The default state of a nullable reference is *maybe-null*. Non-nullable reference types should always be safe to dereference because their *null-state* is *not-null*. To enforce that rule, the compiler issues warnings if a non-nullable reference type isn't initialized to a non-null value. Local variables must be assigned where they're declared. Every field must be assigned a *not-null* value, in a field initializer or every constructor. The compiler issues warnings when a non-nullable reference is assigned to a reference whose state is *maybe-null*. Generally, a non-nullable reference is *not-null* and no warnings are issued when those variables are dereferenced. diff --git a/docs/csharp/language-reference/keywords/static.md b/docs/csharp/language-reference/keywords/static.md index 3e849576fefdb..1f6d5c43abc9c 100644 --- a/docs/csharp/language-reference/keywords/static.md +++ b/docs/csharp/language-reference/keywords/static.md @@ -39,7 +39,7 @@ To refer to the `static` member `x`, use the fully qualified name, `MyBaseC.MySt Console.WriteLine(MyBaseC.MyStruct.x); ``` -While an instance of a class contains a separate copy of all instance fields of the class, there's only one copy of each `static` field. +While an instance of a class contains a separate copy of all instance fields of the class, there's only one copy of each `static` field. For generic types, each closed generic type has its own copy of static fields. Static fields marked with have one copy per thread. It isn't possible to use [`this`](this.md) to reference `static` methods or property accessors. diff --git a/docs/csharp/language-reference/operators/patterns.md b/docs/csharp/language-reference/operators/patterns.md index 976b3fde1f7ec..2299ac2518eff 100644 --- a/docs/csharp/language-reference/operators/patterns.md +++ b/docs/csharp/language-reference/operators/patterns.md @@ -47,13 +47,15 @@ You use declaration and type patterns to check if the run-time type of an expres A *declaration pattern* with type `T` matches an expression when an expression result is non-null and any of the following conditions are true: -- The run-time type of an expression result is `T`. +- The run-time type of an expression result has an identity conversion to `T`. - The type `T` is a `ref struct` type and there is an identity conversion from the expression to `T`. -- The run-time type of an expression result derives from type `T`, implements interface `T`, or another [implicit reference conversion](~/_csharpstandard/standard/conversions.md#1028-implicit-reference-conversions) exists from it to `T`. The following example demonstrates two cases when this condition is true: +- The run-time type of an expression result derives from type `T`, implements interface `T`, or another [implicit reference conversion](~/_csharpstandard/standard/conversions.md#1028-implicit-reference-conversions) exists from it to `T`. This covers inheritance relationships and interface implementations. The following example demonstrates two cases when this condition is true: :::code language="csharp" source="snippets/patterns/DeclarationAndTypePatterns.cs" id="ReferenceConversion"::: In the preceding example, at the first call to the `GetSourceLabel` method, the first pattern matches an argument value because the argument's run-time type `int[]` derives from the type. At the second call to the `GetSourceLabel` method, the argument's run-time type doesn't derive from the type but implements the interface. -- The run-time type of an expression result is a [nullable value type](../builtin-types/nullable-value-types.md) with the underlying type `T`. -- A [boxing](../../programming-guide/types/boxing-and-unboxing.md#boxing) or [unboxing](../../programming-guide/types/boxing-and-unboxing.md#unboxing) conversion exists from the run-time type of an expression result to type `T`. +- The run-time type of an expression result is a [nullable value type](../builtin-types/nullable-value-types.md) with the underlying type `T` and the is `true`. +- A [boxing](../../programming-guide/types/boxing-and-unboxing.md#boxing) or [unboxing](../../programming-guide/types/boxing-and-unboxing.md#unboxing) conversion exists from the run-time type of an expression result to type `T` when the expression isn't an instance of a `ref struct`. + +Declaration patterns don't consider user-defined conversions or implicit span conversions. The following example demonstrates the last two conditions: diff --git a/docs/csharp/language-reference/operators/type-testing-and-cast.md b/docs/csharp/language-reference/operators/type-testing-and-cast.md index e482d330918a0..4df9e94f4f547 100644 --- a/docs/csharp/language-reference/operators/type-testing-and-cast.md +++ b/docs/csharp/language-reference/operators/type-testing-and-cast.md @@ -39,9 +39,9 @@ Where `E` is an expression that returns a value and `T` is the name of a type or The `is` operator returns `true` when an expression result is non-null and any of the following conditions are true: -- The run-time type of an expression result is `T`. +- The run-time type of an expression result has an identity conversion to `T`. -- The run-time type of an expression result derives from type `T`, implements interface `T`, or another [implicit reference conversion](~/_csharpstandard/standard/conversions.md#1028-implicit-reference-conversions) exists from it to `T`. +- The run-time type of an expression result derives from type `T`, implements interface `T`, or another [implicit reference conversion](~/_csharpstandard/standard/conversions.md#1028-implicit-reference-conversions) exists from it to `T`. This covers inheritance relationships and interface implementations. - The run-time type of an expression result is a [nullable value type](../builtin-types/nullable-value-types.md) with the underlying type `T` and the is `true`. diff --git a/docs/csharp/language-reference/unsafe-code.md b/docs/csharp/language-reference/unsafe-code.md index f3e4f95b032de..a6fb577315963 100644 --- a/docs/csharp/language-reference/unsafe-code.md +++ b/docs/csharp/language-reference/unsafe-code.md @@ -22,7 +22,7 @@ C# supports an [`unsafe`](keywords/unsafe.md) context, in which you can write *u Unsafe code has the following properties: - Methods, types, and code blocks can be defined as unsafe. -- In some cases, unsafe code can increase an application's performance by removing array bounds checks. +- In some cases, unsafe code can increase an application's performance by enabling direct memory access through pointers to avoid array bounds checks. - Unsafe code is required when you call native functions that require pointers. - Using unsafe code introduces security and stability risks. - The code that contains unsafe blocks must be compiled with the [**AllowUnsafeBlocks**](compiler-options/language.md#allowunsafeblocks) compiler option. diff --git a/docs/csharp/misc/cs1914.md b/docs/csharp/misc/cs1914.md index 8ddf70ddf4a11..7aa405d5377e4 100644 --- a/docs/csharp/misc/cs1914.md +++ b/docs/csharp/misc/cs1914.md @@ -12,7 +12,7 @@ ms.assetid: e61361b6-4660-41fd-a574-cc48e1b3873c Static field 'name' cannot be assigned in an object initializer - Object initializers by definition initialize objects, or instances, of classes. They cannot be used to initialize a `static` field of a type. No matter how many instances of a class are created, there is only one copy of a `static` field. + Object initializers by definition initialize objects, or instances, of classes. They cannot be used to initialize a `static` field of a type. No matter how many instances of a class are created, there is only one copy of a `static` field. For generic types, each closed generic type has its own copy of static fields. Static fields marked with have one copy per thread. ## To correct this error diff --git a/docs/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members.md b/docs/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members.md index a23fbbb92f42b..f898e3e72b42b 100644 --- a/docs/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members.md +++ b/docs/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members.md @@ -59,7 +59,7 @@ Console.WriteLine(Math.Round(Math.Abs(dub))); ## Static Members - A non-static class can contain static methods, fields, properties, or events. The static member is callable on a class even when no instance of the class exists. The static member is always accessed by the class name, not the instance name. Only one copy of a static member exists, regardless of how many instances of the class are created. Static methods and properties can't access non-static fields and events in their containing type, and they can't access an instance variable of any object unless it's explicitly passed in a method parameter. + A non-static class can contain static methods, fields, properties, or events. The static member is callable on a class even when no instance of the class exists. The static member is always accessed by the class name, not the instance name. Only one copy of a static member exists, regardless of how many instances of the class are created. For generic types, each closed generic type has its own copy of static members. Static fields marked with have one copy per thread. Static methods and properties can't access non-static fields and events in their containing type, and they can't access an instance variable of any object unless it's explicitly passed in a method parameter. It's more typical to declare a non-static class with some static members, than to declare an entire class as static. Two common uses of static fields are to keep a count of the number of objects that are instantiated, or to store a value that must be shared among all instances. diff --git a/docs/csharp/roslyn-sdk/syntax-visualizer.md b/docs/csharp/roslyn-sdk/syntax-visualizer.md index 640639f9e4b8c..7c6d2739e3c10 100644 --- a/docs/csharp/roslyn-sdk/syntax-visualizer.md +++ b/docs/csharp/roslyn-sdk/syntax-visualizer.md @@ -88,15 +88,15 @@ The property grid in the visualizer updates as shown in the following figure: Th ![Symbol properties in Syntax Visualizer](media/syntax-visualizer/symbol-properties.png) -Try **View TypeSymbol (if any)** for the same **AddExpression** node. The property grid in the visualizer updates as shown in the following figure, indicating that the type of the selected expression is `Int32`. +Right-click the same **AddExpression** node and select **View TypeSymbol (if any)**. The property grid in the visualizer updates as shown in the following figure, indicating that the type of the selected expression is `Int32`. ![TypeSymbol properties](media/syntax-visualizer/type-symbol-properties.png) -Try **View Converted TypeSymbol (if any)** for the same **AddExpression** node. The property grid updates indicating that although the type of the expression is `Int32`, the converted type of the expression is `Double` as shown in the following figure. This node includes converted type symbol information because the `Int32` expression occurs in a context where it must be converted to a `Double`. This conversion satisfies the `Double` type specified for the variable `x` on the left-hand side of the assignment operator. +Right-click the same **AddExpression** node and select **View Converted TypeSymbol (if any)**. The property grid updates indicating that although the type of the expression is `Int32`, the converted type of the expression is `Double` as shown in the following figure. This node includes converted type symbol information because the `Int32` expression occurs in a context where it must be converted to a `Double`. This conversion satisfies the `Double` type specified for the variable `x` on the left-hand side of the assignment operator. ![Converted TypeSymbol properties](media/syntax-visualizer/converted-type-symbol-properties.png) -Finally, try **View Constant Value (if any)** for the same **AddExpression** node. The property grid shows that the value of the expression is a compile time constant with value `2`. +Finally, right-click the same **AddExpression** node and select **View Constant Value (if any)**. The property grid shows that the value of the expression is a compile time constant with value `2`. ![A constant value](media/syntax-visualizer/constant-value.png) @@ -118,17 +118,17 @@ This code introduces an alias named `C` that maps to the type `System.Console` a ![Properties for the symbol `C` in Syntax Visualizer](media/syntax-visualizer/symbol-visual-basic.png) -Try **View AliasSymbol (if any)** for the same **IdentifierName** node. The property grid indicates the identifier is an alias with name `C` that is bound to the `System.Console` target. In other words, the property grid provides information regarding the **AliasSymbol** corresponding to the identifier `C`. +Right-click the same **IdentifierName** node and select **View AliasSymbol (if any)**. The property grid indicates the identifier is an alias with name `C` that is bound to the `System.Console` target. In other words, the property grid provides information regarding the **AliasSymbol** corresponding to the identifier `C`. ![AliasSymbol properties](media/syntax-visualizer/alias-symbol.png) -Inspect the symbol corresponding to any declared type, method, property. Select the corresponding node in the visualizer and click on **View Symbol (if any)**. Select the method `Sub Main()`, including the body of the method. Click on **View Symbol (if any)** for the corresponding **SubBlock** node in the visualizer. The property grid shows the **MethodSymbol** for this **SubBlock** has name `Main` with return type `Void`. +Inspect the symbol corresponding to any declared type, method, property. Select the corresponding node in the visualizer and right-click to access **View Symbol (if any)**. Select the method `Sub Main()`, including the body of the method. Right-click the corresponding **SubBlock** node in the visualizer and select **View Symbol (if any)**. The property grid shows the **MethodSymbol** for this **SubBlock** has name `Main` with return type `Void`. ![Viewing symbol for a method declaration](media/syntax-visualizer/method-symbol.png) The above Visual Basic examples can be easily replicated in C#. Type `using C = System.Console;` in place of `Imports C = System.Console` for the alias. The preceding steps in C# yield identical results in the visualizer window. -Semantic inspection operations are only available on nodes. They are not available on tokens or trivia. Not all nodes have interesting semantic information to inspect. When a node doesn't have interesting semantic information, clicking on **View \* Symbol (if any)** shows a blank property grid. +Semantic inspection operations are only available on nodes. They are not available on tokens or trivia. Not all nodes have interesting semantic information to inspect. When a node doesn't have interesting semantic information, right-clicking and selecting **View \* Symbol (if any)** shows a blank property grid. You can read more about APIs for performing semantic analysis in the [Work with semantics](work-with-semantics.md) overview document. diff --git a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md index 3d36b4ca0df61..73f8d4ac449c8 100644 --- a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md +++ b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md @@ -115,7 +115,7 @@ context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); Replace it with the following line: -[!code-csharp[Register the node action](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#RegisterNodeAction "Register a node action")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="RegisterNodeAction"::: After that change, you can delete the `AnalyzeSymbol` method. This analyzer examines , not statements. Notice that `AnalyzeNode` has red squiggles under it. The code you just added references an `AnalyzeNode` method that hasn't been declared. Declare that method using the following code: @@ -127,7 +127,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context) Change the `Category` to ":::no-loc text="Usage":::" in *MakeConstAnalyzer.cs* as shown in the following code: -[!code-csharp[Category constant](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#Category "Change category to Usage")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="Category"::: ## Find local declarations that could be const @@ -140,11 +140,11 @@ Console.WriteLine(x); The first step is to find local declarations. Add the following code to `AnalyzeNode` in *MakeConstAnalyzer.cs*: -[!code-csharp[localDeclaration variable](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#LocalDeclaration "Add localDeclaration variable")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="LocalDeclaration"::: This cast always succeeds because your analyzer registered for changes to local declarations, and only local declarations. No other node type triggers a call to your `AnalyzeNode` method. Next, check the declaration for any `const` modifiers. If you find them, return immediately. The following code looks for any `const` modifiers on the local declaration: -[!code-csharp[bail-out on const keyword](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#BailOutOnConst "bail-out on const keyword")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="BailOutOnConst"::: Finally, you need to check that the variable could be `const`. That means making sure it is never assigned after it is initialized. @@ -166,7 +166,7 @@ if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol)) The code just added ensures that the variable isn't modified, and can therefore be made `const`. It's time to raise the diagnostic. Add the following code as the last line in `AnalyzeNode`: -[!code-csharp[Call ReportDiagnostic](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#ReportDiagnostic "Call ReportDiagnostic")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="ReportDiagnostic"::: You can check your progress by pressing F5 to run your analyzer. You can load the console application you created earlier and then add the following test code: @@ -202,11 +202,11 @@ Next, delete the `MakeUppercaseAsync` method. It no longer applies. All code fix providers derive from . They all override to report available code fixes. In `RegisterCodeFixesAsync`, change the ancestor node type you're searching for to a to match the diagnostic: -[!code-csharp[Find local declaration node](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#FindDeclarationNode "Find the local declaration node that raised the diagnostic")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs" id="FindDeclarationNode"::: Next, change the last line to register a code fix. Your fix will create a new document that results from adding the `const` modifier to an existing declaration: -[!code-csharp[Register the new code fix](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#RegisterCodeFix "Register the new code fix")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs" id="RegisterCodeFix"::: You'll notice red squiggles in the code you just added on the symbol `MakeConstAsync`. Add a declaration for `MakeConstAsync` like the following code: @@ -222,7 +222,7 @@ Your new `MakeConstAsync` method will transform the F5 to run the analyzer project in a second instance of Visual Studio. In the second Visual Studio instance, create a new C# Console Application project and add a few local variable declarations initialized with constant values to the Main method. You'll see that they are reported as warnings as below. @@ -277,37 +277,39 @@ The template uses [Microsoft.CodeAnalysis.Testing](https://github.com/dotnet/ros Replace the template tests in the `MakeConstUnitTest` class with the following test method: -[!code-csharp[test method for fix test](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#FirstFixTest "test method for fix test")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="FirstFixTest"::: Run this test to make sure it passes. In Visual Studio, open the **Test Explorer** by selecting **Test** > **Windows** > **Test Explorer**. Then select **Run All**. ## Create tests for valid declarations -As a general rule, analyzers should exit as quickly as possible, doing minimal work. Visual Studio calls registered analyzers as the user edits code. Responsiveness is a key requirement. There are several test cases for code that should not raise your diagnostic. Your analyzer already handles one of those tests, the case where a variable is assigned after being initialized. Add the following test method to represent that case: +As a general rule, analyzers should exit as quickly as possible, doing minimal work. Visual Studio calls registered analyzers as the user edits code. Responsiveness is a key requirement. There are several test cases for code that should not raise your diagnostic. Your analyzer already handles several of those tests. Add the following test methods to represent those cases: -[!code-csharp[variable assigned](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VariableAssigned "a variable that is assigned after being initialized won't raise the diagnostic")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="VariableAssigned"::: -This test passes as well. Next, add test methods for conditions you haven't handled yet: +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="AlreadyConst"::: -- Declarations that are already `const`, because they are already const: +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="NoInitializer"::: - [!code-csharp[already const declaration](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#AlreadyConst "a declaration that is already const should not raise the diagnostic")] +These tests pass because your analyzer already handles these conditions: -- Declarations that have no initializer, because there is no value to use: +- Variables assigned after initialization are detected by data flow analysis. +- Declarations that are already `const` are filtered out by checking for the `const` keyword. +- Declarations with no initializer are handled by the data flow analysis that detects assignments outside the declaration. - [!code-csharp[declarations that have no initializer](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#NoInitializer "a declaration that has no initializer should not raise the diagnostic")] +Next, add test methods for conditions you haven't handled yet: - Declarations where the initializer is not a constant, because they can't be compile-time constants: - [!code-csharp[declarations where the initializer isn't const](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#InitializerNotConstant "a declaration where the initializer is not a compile-time constant should not raise the diagnostic")] + :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="InitializerNotConstant"::: It can be even more complicated because C# allows multiple declarations as one statement. Consider the following test case string constant: -[!code-csharp[multiple initializers](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#MultipleInitializers "A declaration can be made constant only if all variables in that statement can be made constant")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="MultipleInitializers"::: The variable `i` can be made constant, but the variable `j` cannot. Therefore, this statement cannot be made a const declaration. -Run your tests again, and you'll see these new test cases fail. +Run your tests again, and you'll see these last two test cases fail. ## Update your analyzer to ignore correct declarations @@ -374,33 +376,33 @@ The first `foreach` loop examines each variable declaration using syntactic anal You're almost done. There are a few more conditions for your analyzer to handle. Visual Studio calls analyzers while the user is writing code. It's often the case that your analyzer will be called for code that doesn't compile. The diagnostic analyzer's `AnalyzeNode` method does not check to see if the constant value is convertible to the variable type. So, the current implementation will happily convert an incorrect declaration such as `int i = "abc"` to a local constant. Add a test method for this case: -[!code-csharp[Mismatched types don't raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsInvalid "When the variable type and the constant type don't match, there's no diagnostic")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="DeclarationIsInvalid"::: In addition, reference types are not handled properly. The only constant value allowed for a reference type is `null`, except in the case of , which allows string literals. In other words, `const string s = "abc"` is legal, but `const object s = "abc"` is not. This code snippet verifies that condition: -[!code-csharp[Reference types don't raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsntString "When the variable type is a reference type other than string, there's no diagnostic")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="DeclarationIsntString"::: To be thorough, you need to add another test to make sure that you can create a constant declaration for a string. The following snippet defines both the code that raises the diagnostic, and the code after the fix has been applied: -[!code-csharp[string reference types raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#ConstantIsString "When the variable type is string, it can be constant")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="ConstantIsString"::: Finally, if a variable is declared with the `var` keyword, the code fix does the wrong thing and generates a `const var` declaration, which is not supported by the C# language. To fix this bug, the code fix must replace the `var` keyword with the inferred type's name: -[!code-csharp[var references need to use the inferred types](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VarDeclarations "Declarations made using var must have the type replaced with the inferred type")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="VarDeclarations"::: Fortunately, all of the above bugs can be addressed using the same techniques that you just learned. To fix the first bug, first open *MakeConstAnalyzer.cs* and locate the foreach loop where each of the local declaration's initializers are checked to ensure that they're assigned with constant values. Immediately _before_ the first foreach loop, call `context.SemanticModel.GetTypeInfo()` to retrieve detailed information about the declared type of the local declaration: -[!code-csharp[Retrieve type information](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#VariableConvertedType "Retrieve type information")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="VariableConvertedType"::: Then, inside your `foreach` loop, check each initializer to make sure it's convertible to the variable type. Add the following check after ensuring that the initializer is a constant: -[!code-csharp[Ensure non-user-defined conversion](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#BailOutOnUserDefinedConversion "Bail-out on user-defined conversion")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="BailOutOnUserDefinedConversion"::: The next change builds upon the last one. Before the closing curly brace of the first foreach loop, add the following code to check the type of the local declaration when the constant is a string or null. -[!code-csharp[Handle special cases](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#HandleSpecialCases "Handle special cases")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="HandleSpecialCases"::: You must write a bit more code in your code fix provider to replace the `var` keyword with the correct type name. Return to *MakeConstCodeFixProvider.cs*. The code you'll add does the following steps: @@ -412,7 +414,7 @@ You must write a bit more code in your code fix provider to replace the `var` ke That sounds like a lot of code. It's not. Replace the line that declares and initializes `newLocal` with the following code. It goes immediately after the initialization of `newModifiers`: -[!code-csharp[Replace Var designations](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#ReplaceVar "Replace a var designation with the explicit type")] +:::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs" id="ReplaceVar"::: You'll need to add one `using` directive to use the type: diff --git a/docs/csharp/tour-of-csharp/overview.md b/docs/csharp/tour-of-csharp/overview.md index 12ce5c50d3659..76d4fbaa509e0 100644 --- a/docs/csharp/tour-of-csharp/overview.md +++ b/docs/csharp/tour-of-csharp/overview.md @@ -42,7 +42,7 @@ Beginning with C# 14 and .NET 10, you can create *file based programs*, which si :::code language="csharp" source="./snippets/file-based-programs/hello-world.cs"::: -The first line of the program contains the `#!` sequence for unix shells. The location of the `dotnet` CLI can vary on different distributions. On any unix system, if you set the *execute* (`+x`) permission on a C# file, you can run the C# file from the command line: +The first line of the program contains the `#!` sequence (shebang) for unix shells. The location of the `dotnet` CLI can vary on different distributions. On any unix system, if you set the *execute* (`+x`) permission on such a C# file that contains the shebang directive, you can run the C# file directly from the command line: ```bash ./hello-world.cs diff --git a/docs/csharp/tutorials/nullable-reference-types.md b/docs/csharp/tutorials/nullable-reference-types.md index 04d01857d0102..8e84991d7cb64 100644 --- a/docs/csharp/tutorials/nullable-reference-types.md +++ b/docs/csharp/tutorials/nullable-reference-types.md @@ -124,7 +124,7 @@ namespace NullableIntroduction } ``` -As before, you must initialize the list object to a non-null value or the compiler issues a warning. There are no null checks in the second overload of `AddQuestion` because they aren't needed: You've declared that variable to be non-nullable. Its value can't be `null`. +As before, you must initialize the list object to a non-null value or the compiler issues a warning. There are no null checks in the second overload of `AddQuestion` because the compiler helps enforce the non-nullable contract: You've declared that variable to be non-nullable. While the compiler warns about potential null assignments, runtime null values are still possible. For public APIs, consider adding argument validation even for non-nullable reference types, since client code might not have nullable reference types enabled or could intentionally pass null. Switch to *Program.cs* in your editor and replace the contents of `Main` with the following lines of code: @@ -203,7 +203,7 @@ Finally, add the following loop at the bottom of the `Main` method: :::code language="csharp" source="./snippets/NullableIntroduction/Program.cs" id="WriteAnswers"::: -You don't need any `null` checks in this code because you've designed the underlying interfaces so that they all return non-nullable reference types. +You don't need any `null` checks in this code because you've designed the underlying interfaces so that they all return non-nullable reference types. The compiler's static analysis helps ensure these design contracts are followed. ## Get the code diff --git a/docs/csharp/versioning.md b/docs/csharp/versioning.md index ff22006f2435c..c3b145ae5d737 100644 --- a/docs/csharp/versioning.md +++ b/docs/csharp/versioning.md @@ -32,6 +32,138 @@ The most basic approach to SemVer is the 3 component format `MAJOR.MINOR.PATCH`, - `MINOR` is incremented when you add functionality in a backwards-compatible manner - `PATCH` is incremented when you make backwards-compatible bug fixes +#### Understand version increments with examples + +To help clarify when to increment each version number, here are concrete examples: + +### MAJOR version increments (incompatible API changes) + +These changes require users to modify their code to work with the new version: + +- Removing a public method or property: + + ```csharp + // Version 1.0.0 + public class Calculator + { + public int Add(int a, int b) => a + b; + public int Subtract(int a, int b) => a - b; // This method exists + } + + // Version 2.0.0 - MAJOR increment required + public class Calculator + { + public int Add(int a, int b) => a + b; + // Subtract method removed - breaking change! + } + ``` + +- Changing method signatures: + + ```csharp + // Version 1.0.0 + public void SaveFile(string filename) { } + + // Version 2.0.0 - MAJOR increment required + public void SaveFile(string filename, bool overwrite) { } // Added required parameter + ``` + +- Changing the behavior of existing methods in ways that break expectations: + + ```csharp + // Version 1.0.0 - returns null when file not found + public string ReadFile(string path) => File.Exists(path) ? File.ReadAllText(path) : null; + + // Version 2.0.0 - MAJOR increment required + public string ReadFile(string path) => File.ReadAllText(path); // Now throws exception when file not found + ``` + +### MINOR version increments (backwards-compatible functionality) + +These changes add new features without breaking existing code: + +- Adding new public methods or properties: + + ```csharp + // Version 1.0.0 + public class Calculator + { + public int Add(int a, int b) => a + b; + } + + // Version 1.1.0 - MINOR increment + public class Calculator + { + public int Add(int a, int b) => a + b; + public int Multiply(int a, int b) => a * b; // New method added + } + ``` + +- Adding new overloads: + + ```csharp + // Version 1.0.0 + public void Log(string message) { } + + // Version 1.1.0 - MINOR increment + public void Log(string message) { } // Original method unchanged + public void Log(string message, LogLevel level) { } // New overload added + ``` + +- Adding optional parameters to existing methods: + + ```csharp + // Version 1.0.0 + public void SaveFile(string filename) { } + + // Version 1.1.0 - MINOR increment + public void SaveFile(string filename, bool overwrite = false) { } // Optional parameter + ``` + + > [!NOTE] + > This is a *source compatible change*, but a *binary breaking change*. Users of this library must recompile for it to work correctly. Many libraries would consider this only in *major* version changes, not *minor* version changes. + +### PATCH version increments (backwards-compatible bug fixes) + +These changes fix issues without adding new features or breaking existing functionality: + +- Fixing a bug in an existing method's implementation: + + ```csharp + // Version 1.0.0 - has a bug + public int Divide(int a, int b) + { + return a / b; // Bug: doesn't handle division by zero + } + + // Version 1.0.1 - PATCH increment + public int Divide(int a, int b) + { + if (b == 0) throw new ArgumentException("Cannot divide by zero"); + return a / b; // Bug fixed, behavior improved but API unchanged + } + ``` + +- Performance improvements that don't change the API: + + ```csharp + // Version 1.0.0 + public List SortNumbers(List numbers) + { + return numbers.OrderBy(x => x).ToList(); // Slower implementation + } + + // Version 1.0.1 - PATCH increment + public List SortNumbers(List numbers) + { + var result = new List(numbers); + result.Sort(); // Faster implementation, same API + return result; + } + ``` + +The key principle is: if existing code can use your new version without any changes, it's a MINOR or PATCH update. If existing code needs to be modified to work with your new version, it's a MAJOR update. + There are also ways to specify other scenarios, for example, pre-release versions, when applying version information to your .NET library. ### Backwards Compatibility diff --git a/docs/framework/release-notes/2025/07-08-july-cumulative-update.md b/docs/framework/release-notes/2025/07-08-july-cumulative-update.md new file mode 100644 index 0000000000000..9e2be870ff0fd --- /dev/null +++ b/docs/framework/release-notes/2025/07-08-july-cumulative-update.md @@ -0,0 +1,87 @@ +--- +title: July 2025 security and quality rollup +description: Learn about the improvements in the .NET Framework July 2025 security and quality rollup. +ms.date: 07/08/2025 +--- +# July 2025 security and quality rollup + +_Released July 8, 2025_ + +## Summary of what's new in this release + +- [Security improvements](#security-improvements) +- [Quality and reliability improvements](#quality-and-reliability-improvements) + +### Security improvements + +There are no new security improvements in this release. This update is cumulative and contains all previously released security improvements. + +### Quality and reliability improvements + +This release contains the following quality and reliability improvements. + +#### .NET Libraries + +Addresses an issue with concurrency in and (*Applies to: .NET Framework 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1.*) + +#### .NET Runtime + +Addresses an issue where interactions between .NET Framework DLL loads and OS code integrity enforcement policy trigger error reporting dialog boxes. (*Applies to: .NET Framework 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1.*) + +## Known issues + +This release contains no known issues. + +## Summary tables + +| Product version | Cumulative update | +| --- | --- | +| **Windows 11, version 24H2** | | +| .NET Framework 3.5, 4.8.1 | [5056579](https://support.microsoft.com/kb/5056579) | +| **Microsoft server operating system, version 24H2** | | +| .NET Framework 3.5, 4.8.1 | [5056579](https://support.microsoft.com/kb/5056579) | +| **Microsoft server operating system, version 23H2** | | +| .NET Framework 3.5, 4.8.1 | [5062062](https://support.microsoft.com/kb/5062062) | +| **Windows 11, version 22H2 and Windows 11, version 23H2** | | +| .NET Framework 3.5, 4.8.1 | [5056580](https://support.microsoft.com/kb/5056580) | +| **Microsoft server operating system, version 22H2** | **[5062159](https://support.microsoft.com/kb/5062159)** | +| .NET Framework 3.5, 4.8 | [5055169](https://support.microsoft.com/kb/5055169) | +| .NET Framework 3.5, 4.8.1 | [5054693](https://support.microsoft.com/kb/5054693) | +| **Microsoft server operating system, version 21H2** | **[5062159](https://support.microsoft.com/kb/5062159)** | +| .NET Framework 3.5, 4.8 | [5062063](https://support.microsoft.com/kb/5062063) | +| .NET Framework 3.5, 4.8.1 | [5054693](https://support.microsoft.com/kb/5054693) | +| **Windows 10, version 22H2** | **[5062154](https://support.microsoft.com/kb/5062154)** | +| .NET Framework 3.5, 4.8 | [5056577](https://support.microsoft.com/kb/5056577) | +| .NET Framework 3.5, 4.8.1 | [5056578](https://support.microsoft.com/kb/5056578) | +| **Windows 10, version 21H2** | **[5062154](https://support.microsoft.com/kb/5062154)** | +| .NET Framework 3.5, 4.8 | [5056577](https://support.microsoft.com/kb/5056577) | +| .NET Framework 3.5, 4.8.1 | [5056578](https://support.microsoft.com/kb/5056578) | +| **Windows 10 1809 and Windows Server 2019** | **[5062152](https://support.microsoft.com/kb/5062152)** | +| .NET Framework 3.5, 4.7.2 | [5062070](https://support.microsoft.com/kb/5062070) | +| .NET Framework 3.5, 4.8 | [5062068](https://support.microsoft.com/kb/5062068) | +| **Windows 10 1607 and Windows Server 2016** | | +| .NET Framework 3.5, 4.6.2, 4.7, 4.7.1, 4.7.2 | [5062560](https://support.microsoft.com/kb/5062560) | +| .NET Framework 4.8 | [5062064](https://support.microsoft.com/kb/5062064) | + +The following table is for earlier Windows and Windows Server versions for Security and Quality Rollup updates.   + +| Product version | Security and quality rollup | +| --- | --- | +| **Windows Server 2012 R2** | **[5062157](https://support.microsoft.com/kb/5062157)** | +| .NET Framework 3.5 | [5044012](https://support.microsoft.com/kb/5044012) | +| .NET Framework 4.6.2, 4.7, 4.7.1, 4.7.2 | [5062073](https://support.microsoft.com/kb/5062073) | +| .NET Framework 4.8 | [5062067](https://support.microsoft.com/kb/5062067) | +| **Windows Server 2012** | **[5062156](https://support.microsoft.com/kb/5062156)** | +| .NET Framework 3.5 | [5044009](https://support.microsoft.com/kb/5044009) | +| .NET Framework 4.6.2, 4.7, 4.7.1, 4.7.2 | [5062072](https://support.microsoft.com/kb/5062072) | +| .NET Framework 4.8 | [5062066](https://support.microsoft.com/kb/5062066) | +| **Windows Server 2008 R2** | **[5062155](https://support.microsoft.com/kb/5062155)** | +| .NET Framework 3.5.1 | [5044011](https://support.microsoft.com/kb/5044011) | +| .NET Framework 4.6.2, 4.7, 4.7.1, 4.7.2 | [5062071](https://support.microsoft.com/kb/5062071) | +| .NET Framework 4.8 |[5062065](https://support.microsoft.com/kb/5062065) | +| **Windows Server 2008** | **[5055687](https://support.microsoft.com/kb/5055687)** | +| .NET Framework 2.0, 3.0 | [5044010](https://support.microsoft.com/kb/5044010) | +| .NET Framework 3.5 SP1 | [5040673](https://support.microsoft.com/kb/5040673) | +| .NET Framework 4.6.2 | [5062071](https://support.microsoft.com/kb/5062071) | + +The operating system row lists a KB that is used for update offering purposes. When the operating system KB is offered, the applicability logic determines the specific .NET Framework updates to install. Updates for individual .NET Framework versions are installed based on the version of .NET Framework that's already present on the device. Because of this, the operating system KB isn't expected to be listed as **installed updates** on the device. The expected updates to be installed are the .NET Framework-specific version updates listed in the table above. diff --git a/docs/framework/toc.yml b/docs/framework/toc.yml index 633c25e4ef557..c63cd9ea502e0 100644 --- a/docs/framework/toc.yml +++ b/docs/framework/toc.yml @@ -697,6 +697,8 @@ items: href: release-notes/2025/04-08-april-cumulative-update.md - name: April 2025 cumulative update preview href: release-notes/2025/04-22-april-cumulative-update-preview.md + - name: July 2025 cumulative update + href: release-notes/2025/07-08-july-cumulative-update.md - name: What's new href: whats-new/index.md - name: What's new in accessibility diff --git a/docs/standard/serialization/system-text-json/source-generation.md b/docs/standard/serialization/system-text-json/source-generation.md index 2507fd90ca371..bcd19481182cd 100644 --- a/docs/standard/serialization/system-text-json/source-generation.md +++ b/docs/standard/serialization/system-text-json/source-generation.md @@ -229,7 +229,7 @@ You can combine contracts from multiple source-generated contexts inside a singl ```csharp var options = new JsonSerializerOptions { - TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default); + TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default), }; ```