diff --git a/docs/csharp/language-reference/attributes/general.md b/docs/csharp/language-reference/attributes/general.md index 0eb6d32d2418f..4e99b4c3cd177 100644 --- a/docs/csharp/language-reference/attributes/general.md +++ b/docs/csharp/language-reference/attributes/general.md @@ -1,6 +1,6 @@ --- title: "C# reserved attributes: Miscellaneous" -ms.date: 03/18/2021 +ms.date: 07/02/2021 description: "Learn about attributes that affect code generated by the compiler: the Conditional, Obsolete, AttributeUsage, ModuleInitializer, and SkipLocalsInit attributes." --- # Reserved attributes: Miscellaneous @@ -19,33 +19,33 @@ If the `TRACE_ON` identifier isn't defined, the trace output isn't displayed. Ex The `Conditional` attribute is often used with the `DEBUG` identifier to enable trace and logging features for debug builds but not in release builds, as shown in the following example: -:::code language="csharp" source="snippets/ConditionalExamples.cs" id="SnippetConditional" ::: +:::code language="csharp" source="snippets/ConditionalExamples.cs" ID="SnippetConditional" ::: -When a method marked conditional is called, the presence or absence of the specified preprocessing symbol determines whether the call is included or omitted. If the symbol is defined, the call is included; otherwise, the call is omitted. A conditional method must be a method in a class or struct declaration and must have a `void` return type. Using `Conditional` is cleaner, more elegant, and less error-prone than enclosing methods inside `#if…#endif` blocks. +When a method marked conditional is called, the presence or absence of the specified preprocessing symbol determines whether the compiler includes or omits calls to the method. If the symbol is defined, the call is included; otherwise, the call is omitted. A conditional method must be a method in a class or struct declaration and must have a `void` return type. Using `Conditional` is cleaner, more elegant, and less error-prone than enclosing methods inside `#if…#endif` blocks. -If a method has multiple `Conditional` attributes, a call to the method is included if at one or more conditional symbols is defined (the symbols are logically linked together by using the OR operator). In the following example, the presence of either `A` or `B` results in a method call: +If a method has multiple `Conditional` attributes, compiler includes calls to the method if one or more conditional symbols are defined (the symbols are logically linked together by using the OR operator). In the following example, the presence of either `A` or `B` results in a method call: -:::code language="csharp" source="snippets/ConditionalExamples.cs" id="SnippetMultipleConditions" ::: +:::code language="csharp" source="snippets/ConditionalExamples.cs" ID="SnippetMultipleConditions" ::: ### Using `Conditional` with attribute classes The `Conditional` attribute can also be applied to an attribute class definition. In the following example, the custom attribute `Documentation` will only add information to the metadata if `DEBUG` is defined. -:::code language="csharp" source="snippets/ConditionalExamples.cs" id="SnippetConditionalConditionalAttribute" ::: +:::code language="csharp" source="snippets/ConditionalExamples.cs" ID="SnippetConditionalConditionalAttribute" ::: ## `Obsolete` attribute The `Obsolete` attribute marks a code element as no longer recommended for use. Use of an entity marked obsolete generates a warning or an error. The `Obsolete` attribute is a single-use attribute and can be applied to any entity that allows attributes. `Obsolete` is an alias for . -In the following example the `Obsolete` attribute is applied to class `A` and to method `B.OldMethod`. Because the second argument of the attribute constructor applied to `B.OldMethod` is set to `true`, this method will cause a compiler error, whereas using class `A` will just produce a warning. Calling `B.NewMethod`, however, produces no warning or error. For example, when you use it with the previous definitions, the following code generates two warnings and one error: +In the following example, the `Obsolete` attribute is applied to class `A` and to method `B.OldMethod`. Because the second argument of the attribute constructor applied to `B.OldMethod` is set to `true`, this method will cause a compiler error, whereas using class `A` will just produce a warning. Calling `B.NewMethod`, however, produces no warning or error. For example, when you use it with the previous definitions, the following code generates two warnings and one error: -:::code language="csharp" source="snippets/ObsoleteExample.cs" id="Snippet1" interactive="try-dotnet" ::: +:::code language="csharp" source="snippets/ObsoleteExample.cs" ID="Snippet1" interactive="try-dotnet" ::: The string provided as the first argument to the attribute constructor will be displayed as part of the warning or error. Two warnings for class `A` are generated: one for the declaration of the class reference, and one for the class constructor. The `Obsolete` attribute can be used without arguments, but including an explanation what to use instead is recommended. In C# 10, you can use constant string interpolation and the `nameof` operator to ensure the names match: -:::code language="csharp" source="snippets/ObsoleteExample.cs" id="Snippet2" ::: +:::code language="csharp" source="snippets/ObsoleteExample.cs" ID="Snippet2" ::: ## `AttributeUsage` attribute @@ -66,36 +66,81 @@ The `AttributeUsage` attribute determines how a custom attribute class can be us The default settings look like the following example when applied explicitly: -:::code language="csharp" source="snippets/NewAttribute.cs" id="SnippetUsageFirst" ::: +:::code language="csharp" source="snippets/NewAttribute.cs" ID="SnippetUsageFirst" ::: In this example, the `NewAttribute` class can be applied to any supported program element. But it can be applied only once to each entity. The attribute is inherited by derived classes when applied to a base class. The and arguments are optional, so the following code has the same effect: -:::code language="csharp" source="snippets/NewAttribute.cs" id="SnippetUsageSecond" ::: +:::code language="csharp" source="snippets/NewAttribute.cs" ID="SnippetUsageSecond" ::: The first argument must be one or more elements of the enumeration. Multiple target types can be linked together with the OR operator, like the following example shows: -:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" id="SnippetDefinePropertyAttribute" ::: +:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" ID="SnippetDefinePropertyAttribute" ::: Beginning in C# 7.3, attributes can be applied to either the property or the backing field for an auto-implemented property. The attribute applies to the property, unless you specify the `field` specifier on the attribute. Both are shown in the following example: -:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" id="SnippetUsePropertyAttribute" ::: +:::code language="csharp" source="snippets/NewPropertyOrFieldAttribute.cs" ID="SnippetUsePropertyAttribute" ::: If the argument is `true`, then the resulting attribute can be applied more than once to a single entity, as shown in the following example: -:::code language="csharp" source="snippets/MultiUseAttribute.cs" id="SnippetMultiUse" ::: +:::code language="csharp" source="snippets/MultiUseAttribute.cs" ID="SnippetMultiUse" ::: In this case, `MultiUseAttribute` can be applied repeatedly because `AllowMultiple` is set to `true`. Both formats shown for applying multiple attributes are valid. If is `false`, then the attribute isn't inherited by classes derived from an attributed class. For example: -:::code language="csharp" source="snippets/NonInheritedAttribute.cs" id="SnippetNonInherited" ::: +:::code language="csharp" source="snippets/NonInheritedAttribute.cs" ID="SnippetNonInherited" ::: In this case `NonInheritedAttribute` isn't applied to `DClass` via inheritance. You can also use these keywords to specify where an attribute should be applied. For example, you can use the `field:` specifier to add an attribute to the backing field of an [auto-implemented property](../../programming-guide/classes-and-structs/properties.md#auto-implemented-properties). Or you can use the `field:`, `property:` or `param:` specifier to apply an attribute to any of the elements generated from a positional record. For an example, see [Positional syntax for property definition](../builtin-types/record.md#positional-syntax-for-property-definition). +## `AsyncMethodBuilder` attribute + +Beginning with C# 7, you add the attribute to a type that can be an async return type. The attribute specifies the type that builds the async method implementation when the specified type is returned from an async method. The `AsyncMethodBuilder` attribute can be applied to a type that: + +* Has an accessible `GetAwaiter` method. +* The object returned by the `GetAwaiter` method implements the interface. + +The constructor to the `AsyncMethodBuilder` attribute specifies the type of the associated builder. The builder must implement the following accessible members: + +* a static `Create()` method that returns the type of the builder. +* a readable `Task` property that returns the async return type. +* a `void SetException(Exception)` method that sets the exception when a task faults. +* a `void SetResult()` or `void SetResult(T result)` method that marks the task as completed and optionally sets the task's result +* a `Start` method with the following API signature: + + ```csharp + void Start(ref TStateMachine stateMachine) + where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine + ``` + +* An `AwaitOnCompleted` method with the following signature: + + ```csharp + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion + where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine + ``` + +* An `AwaitUnsafeOnCompleted` method with the following signature: + + ```csharp + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion + where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine + ``` + +You can learn about async method builders by reading about the following builders supplied by .NET: + +- +- +- +- + +In C# 10.0 and later, the `AsyncMethodBuilder` attribute can be applied to an async method to override the builder for that type. + ## `ModuleInitializer` attribute Starting with C# 9, the `ModuleInitializer` attribute marks a method that the runtime calls when the assembly loads. `ModuleInitializer` is an alias for . @@ -118,19 +163,19 @@ The following example illustrates use of multiple module initializer methods. Th :::code language="csharp" source="snippets/ModuleInitializerExampleModule.cs" ::: -Source code generators sometimes need to generate initialization code. Module initializers provide a standard place for that code to reside. +Source code generators sometimes need to generate initialization code. Module initializers provide a standard place for that code. ## `SkipLocalsInit` attribute Starting in C# 9, the `SkipLocalsInit` attribute prevents the compiler from setting the `.locals init` flag when emitting to metadata. The `SkipLocalsInit` attribute is a single-use attribute and can be applied to a method, a property, a class, a struct, an interface, or a module, but not to an assembly. `SkipLocalsInit` is an alias for . -The `.locals init` flag causes the CLR to initialize all of the local variables declared in a method to their default values. Since the compiler also makes sure that you never use a variable before assigning some value to it, `.locals init` is typically not necessary. However, the extra zero-initialization may have measurable performance impact in some scenarios, such as when you use [stackalloc](../operators/stackalloc.md) to allocate an array on the stack. In those cases, you can add the `SkipLocalsInit` attribute. If applied to a method directly, the attribute affects that method and all its nested functions, including lambdas and local functions. If applied to a type or module, it affects all methods nested inside. This attribute does not affect abstract methods, but it does affect code generated for the implementation. +The `.locals init` flag causes the CLR to initialize all of the local variables declared in a method to their default values. Since the compiler also makes sure that you never use a variable before assigning some value to it, `.locals init` is typically not necessary. However, the extra zero-initialization may have measurable performance impact in some scenarios, such as when you use [stackalloc](../operators/stackalloc.md) to allocate an array on the stack. In those cases, you can add the `SkipLocalsInit` attribute. If applied to a method directly, the attribute affects that method and all its nested functions, including lambdas and local functions. If applied to a type or module, it affects all methods nested inside. This attribute doesn't affect abstract methods, but it does affect code generated for the implementation. This attribute requires the [AllowUnsafeBlocks](../compiler-options/language.md#allowunsafeblocks) compiler option. This is to signal that in some cases code could view unassigned memory (for example, reading from uninitialized stack-allocated memory). The following example illustrates the effect of `SkipLocalsInit` attribute on a method that uses `stackalloc`. The method displays whatever was in memory when the array of integers was allocated. -:::code language="csharp" source="snippets/SkipLocalsInitExample.cs" id="ReadUninitializedMemory"::: +:::code language="csharp" source="snippets/SkipLocalsInitExample.cs" ID="ReadUninitializedMemory"::: To try this code yourself, set the `AllowUnsafeBlocks` compiler option in your *.csproj* file: diff --git a/docs/csharp/programming-guide/concepts/async/async-return-types.md b/docs/csharp/programming-guide/concepts/async/async-return-types.md index a4243d33398bd..d602d83d39a12 100644 --- a/docs/csharp/programming-guide/concepts/async/async-return-types.md +++ b/docs/csharp/programming-guide/concepts/async/async-return-types.md @@ -1,7 +1,7 @@ --- title: Async return types (C#) -description: Learn about the return types that async methods can have in C# with code examples for each type and additional resources. -ms.date: 08/19/2020 +description: Learn about the return types that async methods can have in C# with code examples for each type. +ms.date: 07/02/2021 ms.assetid: ddb2539c-c898-48c1-ad92-245e4a996df8 --- @@ -20,8 +20,8 @@ For more information about async methods, see [Asynchronous programming with asy Several other types also exist that are specific to Windows workloads: - , for async operations limited to Windows. -- , for async actions in UWP that do not return a value. -- , for async actions in UWP that report progress but do not return a value. +- , for async actions in UWP that don't return a value. +- , for async actions in UWP that report progress but don't return a value. - , for async operations in UWP that return a value. - , for async operations in UWP that report progress and return a value. @@ -31,7 +31,7 @@ Async methods that don't contain a `return` statement or that contain a `return` In the following example, the `WaitAndApologizeAsync` method doesn't contain a `return` statement, so the method returns a object. Returning a `Task` enables `WaitAndApologizeAsync` to be awaited. The type doesn't include a `Result` property because it has no return value. -:::code language="csharp" source="snippets/async-return-types/async-returns2.cs" id="TaskReturn"::: +:::code language="csharp" source="snippets/async-return-types/async-returns2.cs" ID="TaskReturn"::: `WaitAndApologizeAsync` is awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous void-returning method. The application of an await operator in this case doesn't produce a value. When the right operand of an `await` is a , the `await` expression produces a result of `T`. When the right operand of an `await` is a , the `await` and its operand are a statement. @@ -39,15 +39,15 @@ You can separate the call to `WaitAndApologizeAsync` from the application of an The following code separates calling the `WaitAndApologizeAsync` method from awaiting the task that the method returns. -:::code language="csharp" source="snippets/async-return-types/async-returns2a.cs" id="AwaitTask"::: +:::code language="csharp" source="snippets/async-return-types/async-returns2a.cs" ID="AwaitTask"::: ## Task\ return type The return type is used for an async method that contains a [return](../../../language-reference/keywords/return.md) statement in which the operand is `TResult`. -In the following example, the `GetLeisureHoursAsync` method contains a `return` statement that returns an integer. Therefore, the method declaration must specify a return type of `Task`. The async method is a placeholder for an operation that returns a . +In the following example, the `GetLeisureHoursAsync` method contains a `return` statement that returns an integer. The method declaration must specify a return type of `Task`. The async method is a placeholder for an operation that returns a . -:::code language="csharp" source="snippets/async-return-types/async-returns1.cs" id="LeisureHours"::: +:::code language="csharp" source="snippets/async-return-types/async-returns1.cs" ID="LeisureHours"::: When `GetLeisureHoursAsync` is called from within an await expression in the `ShowTodaysInfo` method, the await expression retrieves the integer value (the value of `leisureHours`) that's stored in the task returned by the `GetLeisureHours` method. For more information about await expressions, see [await](../../../language-reference/operators/await.md). @@ -58,13 +58,13 @@ You can better understand how `await` retrieves the result from a `Task` by s > > The previous example retrieved the value of the property to block the main thread so that the `Main` method could print the `message` to the console before the application ended. -:::code language="csharp" source="snippets/async-return-types/async-returns1a.cs" id="StoreTask"::: +:::code language="csharp" source="snippets/async-return-types/async-returns1a.cs" ID="StoreTask"::: ## Void return type You use the `void` return type in asynchronous event handlers, which require a `void` return type. For methods other than event handlers that don't return a value, you should return a instead, because an async method that returns `void` can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish. The caller must be independent of any values or exceptions that the async method generates. -The caller of a void-returning async method can't catch exceptions thrown from the method, and such unhandled exceptions are likely to cause your application to fail. If a method that returns a or throws an exception, the exception is stored in the returned task. The exception is rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of or and that calls to the method are awaited. +The caller of a void-returning async method can't catch exceptions thrown from the method. Such unhandled exceptions are likely to cause your application to fail. If a method that returns a or throws an exception, the exception is stored in the returned task. The exception is rethrown when the task is awaited. Make sure that any async method that can produce an exception has a return type of or and that calls to the method are awaited. For more information about how to catch exceptions in async methods, see the [Exceptions in async methods](../../../language-reference/keywords/try-catch.md#exceptions-in-async-methods) section of the [try-catch](../../../language-reference/keywords/try-catch.md) article. @@ -74,7 +74,7 @@ The following example shows the behavior of an async event handler. In the examp ## Generalized async return types and ValueTask\ -Starting with C# 7.0, an async method can return any type that has an accessible `GetAwaiter` method that returns an instance of an *awaiter type*. In addition, the type returned from the `GetAwaiter` method must have the attribute. You can learn more in the feature spec for [Task like return types](~/_csharplang/proposals/csharp-7.0/task-types.md). +Starting with C# 7.0, an async method can return any type that has an accessible `GetAwaiter` method that returns an instance of an *awaiter type*. In addition, the type returned from the `GetAwaiter` method must have the attribute. You can learn more in the article on [Attributes read by the compiler](../../../language-reference/attributes/general.md#asyncmethodbuilder-attribute) or the feature spec for [Task like return types](~/_csharplang/proposals/csharp-7.0/task-types.md). This feature is the complement to [awaitable expressions](~/_csharplang/spec/expressions.md#awaitable-expressions), which describes the requirements for the operand of `await`. Generalized async return types enable the compiler to generate `async` methods that return different types. Generalized async return types enabled performance improvements in the .NET libraries. Because and are reference types, memory allocation in performance-critical paths, particularly when allocations occur in tight loops, can adversely affect performance. Support for generalized return types means that you can return a lightweight value type instead of a reference type to avoid additional memory allocations. @@ -82,13 +82,15 @@ This feature is the complement to [awaitable expressions](~/_csharplang/spec/exp :::code language="csharp" source="snippets/async-return-types/async-valuetask.cs"::: -Writing a generalized async return type is an advanced scenario, and is targeted for use in very specific environments. Consider using the `Task`, `Task` and `ValueTask` types instead, which cover most scenarios for asynchronous code. +Writing a generalized async return type is an advanced scenario, and is targeted for use in specialized environments. Consider using the `Task`, `Task`, and `ValueTask` types instead, which cover most scenarios for asynchronous code. + +In C# 10.0 and later, you can apply the `AsyncMethodBuilder` attribute to an async method (instead of the async return type declaration) to override the builder for that type. Typically you'd apply this attribute to use a different builder provided in the .NET runtime. ## Async streams with IAsyncEnumerable\ Starting with C# 8.0, an async method may return an *async stream*, represented by . An async stream provides a way to enumerate items read from a stream when elements are generated in chunks with repeated asynchronous calls. The following example shows an async method that generates an async stream: -:::code language="csharp" source="snippets/async-return-types/AsyncStreams.cs" id="GenerateAsyncStream"::: +:::code language="csharp" source="snippets/async-return-types/AsyncStreams.cs" ID="GenerateAsyncStream"::: The preceding example reads lines from a string asynchronously. Once each line is read, the code enumerates each word in the string. Callers would enumerate each word using the `await foreach` statement. The method awaits when it needs to asynchronously read the next line from the source string.