diff --git a/docs/core/deploying/trimming/prepare-libraries-for-trimming.md b/docs/core/deploying/trimming/prepare-libraries-for-trimming.md
index 7f08bec781dbd..5f0d7f165bc2a 100644
--- a/docs/core/deploying/trimming/prepare-libraries-for-trimming.md
+++ b/docs/core/deploying/trimming/prepare-libraries-for-trimming.md
@@ -8,360 +8,258 @@ ms.date: 06/12/2023
# Prepare .NET libraries for trimming
-The .NET SDK makes it possible to reduce the size of self-contained apps by [trimming](trim-self-contained.md), which removes unused code from the app and its dependencies. Not all code is compatible with trimming, so .NET 6 provides trim analysis warnings to detect patterns that may break trimmed apps. To resolve warnings originating from the app code, see [resolving trim warnings](#resolve-trim-warnings). This article describes how to prepare libraries for trimming with the aid of these warnings, including recommendations for fixing some common cases.
+The .NET SDK makes it possible to reduce the size of self-contained apps by [trimming](trim-self-contained.md). Trimming removes unused code from the app and its dependencies. Not all code is compatible with trimming. .NET provides trim analysis warnings to detect patterns that may break trimmed apps. This article:
-## Enable library trim warnings
+* Describes how to prepare libraries for trimming.
+* Provides recommendations for [resolving common trimming warnings](#resolve-trim-warnings).
+
+## Prerequisites
-> [!TIP]
-> Ensure you're using the .NET 6 SDK or later for these steps. They will not work correctly in previous versions.
+[.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet) or later.
-There are two ways to find trim warnings in your library:
+To get the most up-to-date trimming warnings and analyzer coverage:
- 1. Enable project-specific trimming using the `IsTrimmable` property.
- 2. Add your library as a reference to a sample app, and trim the sample app.
+* Install and target the .NET 8 SDK or later.
+* When targeting .NET 6 or NET 7, install the .NET 8 SDK or later.
+
+## Enable library trim warnings
-Consider doing both. Project-specific trimming is convenient and shows trim warnings for one project, but relies on the references being marked trim-compatible to see all warnings. Trimming a sample app is more work, but will always show all warnings.
+Trim warnings in a library can be found with either of the following methods:
+
+* Enabling project-specific trimming using the `IsTrimmable` property.
+* Creating a trimming test app that uses the library and enabling trimming for the test app. It's not necessary to reference all the APIs in the library.
+
+We recommend using both approaches. Project-specific trimming is convenient and shows trim warnings for one project, but relies on the references being marked trim-compatible to see all warnings. Trimming a test app is more work, but shows all warnings.
### Enable project-specific trimming
-> [!TIP]
-> To get the latest version of the analyzer with the most coverage, use the [.NET 7 SDK](https://dotnet.microsoft.com/download/dotnet). Note this will only update the tooling used to build your app and doesn't require you to target the .NET 7 runtime.
+### [.NET 6](#tab/net6)
+
+To get the latest version of the analyzer with the most coverage, install the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet) or later. Installing the .NET 8 SDK or later:
+
+* Updates the tooling used to build an app or library and enable trim warnings.
+* Provides significant trimming and analyzer improvements over previous .NET SDKs.
+* Does ***not*** require targeting the .NET 8 runtime. Library authors can continue to target the .NET 6 runtime.
+
+Set `true` in the project file.
+
+### [.NET 7](#tab/net7)
+
+To get the latest version of the analyzer with the most coverage, install the [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet) or later. Installing the .NET 8 SDK or later:
+
+* Updates the tooling used to build an app or library and enable trim warnings.
+* Provides significant trimming and analyzer improvements over previous .NET SDKs.
+* Does ***not*** require targeting the .NET 8 runtime. Libary authors can continue to target the .NET 7 runtime.
+
+Set `true` in the project file.
+
+### [.NET 8+](#tab/net8plus)
+
+Set `true` in the project file.
+
+---
+
+:::code language="xml" source="~/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj.xml" id="snippet" highlight="2":::
+
+Setting `true` marks the assembly as "trimmable" and enables trim warnings. "trimmable" means the project:
+
+* Is considered compatible with trimming.
+* Shouldn't generate trim related warnings when building. When used in a trimmed app, the assembly has its unused members trimmed in the final output.
+
+The `IsTrimmable` property defaults to `true` when configuring a project as AOT-compatible with `true`. For more information, see [AOT-compatibility analyzers](../native-aot/index.md#aot-compatibility-analyzers).
+
+To generate trim warnings without marking the project as trim-compatible, use `true` rather than `true`.
+
+### Show all warnings with test app
+
+To show all analysis warnings for a library, the trimmer must analyze the implementation:
-Set `true` in a `` tag in your library project file. This marks your assembly as "trimmable" and enable trim warnings for that project. Being "trimmable" means your library is considered compatible with trimming and should have no trim warnings when building the library. When used in a trimmed application, the assembly has its unused members trimmed in the final output.
+* Of the library.
+* All dependencies the library uses.
-The `IsTrimmable` property defaults to `true` when configuring a library as AOT-compatible (`true`). For more information, see [AOT-compatibility analyzers](../native-aot/index.md#aot-compatibility-analyzers).
+When building and publishing a library:
-If you want to see trim warnings, but don't want to mark your library as trim-compatible, you can add `true` instead.
+* The implementations of the dependencies aren't available.
+* The available reference assemblies don't have enough information for the trimmer to determine if they're compatible with trimming.
-### Show all warnings with sample application
+Because of the dependency limitations, a self-contained test app which uses the library and its dependencies must be created. The test app includes all the information the trimmer requires to issue warning on trim incompatibilities in:
-To show all analysis warnings for your library, including warnings about dependencies, you need the trimmer to analyze the implementation of your library and the implementations of dependencies your library uses. When building and publishing a library, the implementations of the dependencies aren't available, and the available reference assemblies don't have enough information for the trimmer to determine if they're compatible with trimming. Because of this, you need to create and publish a self-contained sample application, which produces an executable that includes your library and the dependencies it relies on. This executable includes all the information the trimmer requires to warn you about all trim incompatibilities in your library code, and the code that your library references from its dependencies.
+* The library code.
+* The code that the library references from its dependencies.
-> [!NOTE]
-> If your library has significantly different behavior or uses different APIs depending on the target framework of the consumer (for example, using `#if NET7_0`) which might impact trimming, you will need to create a new sample app for each of the target frameworks you want to support trimming for.
+***Note:*** If the library has different behavior depending on the target framework, create a trimming test app for each of the target frameworks that support trimming. For example, if the library uses [conditional compilation](/dotnet/csharp/language-reference/preprocessor-directives#conditional-compilation) such as `#if NET7_0` to change behavior.
-To create your sample app, first create a separate console application project with `dotnet new console` and modify the project file to look like the following. No changes to the source code are necessary. You need to do the following in your project file:
+To create the trimming test app:
-- Set the PublishTrimmed property to `true` with `true` in a `` tag.
-- Add a reference to your library project with `` inside of an `` tag.
-- Specify your library as a trimmer root assembly with `` in an `` tag.
- - This ensures that every part of the library is analyzed. It tells the trimmer that this assembly is a "root," which means the trimmer analyzes the assembly as if everything were used, and traverses all possible code paths that originate from that assembly. This is necessary in case the library has `[AssemblyMetadata("IsTrimmable", "True")]`, which would otherwise let trimming remove the unused library without analyzing it.
-- If your app targets .NET 6, set the `TrimmerDefaultAction` property to `link` with `link` in a `` element.
-- If your app targets .NET 7, [the new default behavior](../../../core/compatibility/deployment/7.0/trim-all-assemblies.md) is what you want, but you can enforce the behavior by adding `full` in a `` tag.
- - This ensures that the trimmer only analyzes the parts of the library's dependencies that are used. It tells the trimmer that any code that isn't part of a "root" can be trimmed if it's unused. Without this option, you would see warnings originating from _any_ part of a dependency that doesn't set `[AssemblyMetadata("IsTrimmable", "True")]`, including parts that are unused by your library.
+* Create a separate console application project.
+* Add a reference to the library.
+* Modify the project similar to the project shown below using the following list:
-##### .csproj file
+If library targets a TFM that is not trimmable, for example `net472` or `netstandard2.0`, there's no benefit to creating a trimming test app. Trimming is only supported for .NET 6 and later.
### [.NET 6](#tab/net6)
-```xml
-
-
-
- Exe
-
- net6.0
- true
-
- link
-
-
-
-
-
-
-
-
-
-```
+* Set `` to `link`.
+* Add `true`.
+* Add a reference to the library project with ``.
+* Specify the library as a trimmer root assembly with ``.
+ * `TrimmerRootAssembly` ensures that every part of the library is analyzed. It tells the trimmer that this assembly is a "root". A "root" assembly means the trimmer analyzes every call in the library and traverses all code paths that originate from that assembly.
+
+### .csproj file
+
+:::code language="xml" source="~/docs/core/deploying/trimming/snippets/MyTestLib6app/XMLFile1.xml":::
### [.NET 7](#tab/net7)
-```xml
-
+* Add `true`.
+* Add a reference to the library project with ``.
+* Specify the library as a trimmer root assembly with ``.
+ * `TrimmerRootAssembly` ensures that every part of the library is analyzed. It tells the trimmer that this assembly is a "root". A "root" assembly means the trimmer analyzes every call in the library and traverses all code paths that originate from that assembly.
-
- Exe
- net7.0
- true
-
+### .csproj file
-
-
-
-
-
+:::code language="xml" source="~/docs/core/deploying/trimming/snippets/ConsoleApp1/ConsoleApp1.csproj":::
-
-```
+**Note:** In the preceding project file, when using .NET 7, replace `net8.0` with `net7.0`.
### [.NET 8+](#tab/net8plus)
-```xml
-
+
+* Add `true`.
+* Add a reference to the library project with ``.
+* Specify the library as a trimmer root assembly with ``.
+ * `TrimmerRootAssembly` ensures that every part of the library is analyzed. It tells the trimmer that this assembly is a "root". A "root" assembly means the trimmer analyzes every call in the library and traverses all code paths that originate from that assembly.
-
- Exe
- net8.0
- true
-
+### .csproj file
-
-
-
-
-
+:::code language="xml" source="~/docs/core/deploying/trimming/snippets/ConsoleApp1/ConsoleApp1.csproj":::
-
-```
+
---
-Once your project file is updated, run `dotnet publish` with the [runtime identifier (RID)](../../rid-catalog.md) you want to target.
+Once the project file is updated, run `dotnet publish` with the target [runtime identifier (RID)](../../rid-catalog.md).
```dotnetcli
dotnet publish -c Release -r
```
-You can also follow the same pattern for multiple libraries. To see trim analysis warnings for more than one library at a time, add them all to the same project as `ProjectReference` and `TrimmerRootAssembly` items. This warns about dependencies if _any_ of the root libraries use a trim-unfriendly API in a dependency. To see warnings that have to do with only a particular library, reference that library only.
+Follow the preceding pattern for multiple libraries. To see trim analysis warnings for more than one library at a time, add them all to the same project as `ProjectReference` and `TrimmerRootAssembly` items. Adding all the libraries to the same project with `ProjectReference` and `TrimmerRootAssembly` items warns about dependencies if ***any*** of the root libraries use a trim-unfriendly API in a dependency. To see warnings that have to do with only a particular library, reference that library only.
-> [!NOTE]
-> The analysis results depend on the implementation details of your dependencies. If you update to a new version of a dependency, this may introduce analysis warnings if the new version added non-understood reflection patterns, even if there were no API changes. In other words, introducing trim analysis warnings to a library is a breaking change when the library is used with `PublishTrimmed`.
+***Note:*** The analysis results depend on the implementation details of the dependencies. Updating to a new version of a dependency may introduce analysis warnings:
+
+* If the new version added non-understood reflection patterns.
+* Even if there were no API changes.
+* Introducing trim analysis warnings is a breaking change when the library is used with `PublishTrimmed`.
## Resolve trim warnings
-The above steps produce warnings about code that may cause problems when used in a trimmed app. Here are a few examples of the most common kinds of warnings you may encounter, with recommendations for fixing them.
+The preceding steps produce warnings about code that may cause problems when used in a trimmed app. The following examples show the most common warnings with recommendations for fixing them.
### RequiresUnreferencedCode
-```csharp
-using System.Diagnostics.CodeAnalysis;
-
-public class MyLibrary
-{
- public static void Method()
- {
- // warning IL2026 : MyLibrary.Method: Using method 'MyLibrary.DynamicBehavior'
- // which has 'RequiresUnreferencedCodeAttribute' can break functionality
- // when trimming application code.
- DynamicBehavior();
- }
-
- [RequiresUnreferencedCode(
- "DynamicBehavior is incompatible with trimming.")]
- static void DynamicBehavior()
- {
- }
-}
-```
+Consider the following code that uses [`[RequiresUnreferencedCode]`](xref:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute) to indicate that the specified method requires dynamic access to code that is not referenced statically, for example, through .
-This means the library calls a method that has explicitly been annotated as incompatible with trimming, using . To get rid of the warning, consider whether `Method` needs to call `DynamicBehavior` to do its job. If so, annotate the caller `Method` with `RequiresUnreferencedCode` as well; this will "bubble up" the warning so that callers of `Method` get a warning instead:
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_1" highlight="12-13":::
-```csharp
-// Warn for calls to Method, but not for Method's call to DynamicBehavior.
-[RequiresUnreferencedCode("Calls DynamicBehavior.")]
-public static void Method()
-{
- DynamicBehavior(); // OK. Doesn't warn now.
-}
-```
+The preceding highlighted code indicates the library calls a method that has explicitly been annotated as incompatible with trimming. To get rid of the warning, consider whether `MyMethod` needs to call `DynamicBehavior`. If so, annotate the caller `MyMethod` with `[RequiresUnreferencedCode]` which propagates the warning so that callers of `MyMethod` get a warning instead:
-Once you have "bubbled up" the attribute all the way to public APIs (so that these warnings are produced only for public methods, if at all), you're done. Apps that call your library will now get warnings if they call those public APIs, but these will no longer produce warnings like `IL2104: Assembly 'MyLibrary' produced trim warnings`.
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class2.cs" id="snippet_RequiresUnreferencedCode" highlight="3":::
-### DynamicallyAccessedMembers
+Once you have propagated up the attribute all the way to public API, apps calling the library:
-```csharp
-using System.Diagnostics.CodeAnalysis;
-
-public class MyLibrary
-{
- static void UseMethods(Type type)
- {
- // warning IL2070: MyLibrary.UseMethods(Type): 'this' argument does not satisfy
- // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
- // 'System.Type.GetMethods()'.
- // The parameter 't' of method 'MyLibrary.UseMethods(Type)' doesn't have
- // matching annotations.
- foreach (var method in type.GetMethods())
- {
- // ...
- }
- }
-}
-```
+* Get warnings only for public methods that aren't trimmable.
+* Don't get warnings like `IL2104: Assembly 'MyLibrary' produced trim warnings`.
-Here, `UseMethods` is calling a reflection method that has a requirement. The requirement states that the type's public methods are available. In this case, you can satisfy the requirement by adding the same requirement to the parameter of `UseMethods`.
+### DynamicallyAccessedMembers
-```csharp
-static void UseMethods(
- // State the requirement in the UseMethods parameter.
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
- Type type)
-{
- // ...
-}
-```
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_DAA1":::
-Now any calls to `UseMethods` produce warnings if they pass in values that don't satisfy the `PublicMethods` requirement. Like with `RequiresUnreferencedCode`, once you have bubbled up such warnings to public APIs, you're done.
+In the preceding code, `UseMethods` is calling a reflection method that has a [`[DynamicallyAccessedMembers]`](xref:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute) requirement. The requirement states that the type's public methods are available. Satisfy the requirement by adding the same requirement to the parameter of `UseMethods`.
-Here's another example where an unknown `Type` flows into the annotated method parameter, this time from a field:
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_DAA2" highlight="3":::
-```csharp
-static Type type;
+Now any calls to `UseMethods` produce warnings if they pass in values that don't satisfy the requirement. Similar to `[RequiresUnreferencedCode]`, once you have propagated up such warnings to public APIs, you're done.
-static void UseMethodsHelper()
-{
- // warning IL2077: MyLibrary.UseMethodsHelper(Type): 'type' argument does not satisfy
- // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
- // 'MyLibrary.UseMethods(Type)'.
- // The field 'System.Type MyLibrary::type' does not have matching annotations.
- UseMethods(type);
-}
-```
+In the following example, an unknown [Type](/dotnet/api/system.type) flows into the annotated method parameter. The unknown `Type` is from a field:
-Similarly, here the problem is that the field `type` is passed into a parameter with these requirements. You can fix it by adding `DynamicallyAccessedMembers` to the field. This warns about code that assigns incompatible values to the field instead. Sometimes this process continues until a public API is annotated, and other times it ends when a concrete type flows into a location with these requirements. For example:
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_UMH":::
-```csharp
-[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
-static Type type;
+Similarly, here the problem is that the field `type` is passed into a parameter with these requirements. It's fixed by adding [`[DynamicallyAccessedMembers]`](xref:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute) to the field. `[DynamicallyAccessedMembers]` warns about code that assigns incompatible values to the field. Sometimes this process continues until a public API is annotated, and other times it ends when a concrete type flows into a location with these requirements. For example:
-static void InitializeTypeField()
-{
- MyLibrary.type = typeof(System.Tuple);
-}
-```
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class3.cs" id="snippet_UMH2" highlight="1":::
In this case, the trim analysis keeps public methods of , and produces further warnings.
## Recommendations
-In general, try to avoid reflection if possible. When using reflection, limit it in scope so that it's reachable only from a small part of the library.
-
-- Avoid using non-understood patterns in places like static constructors that result in the warning propagating to all members of the class.
-- Avoid annotating virtual methods or interface methods, which will require all overrides to have matching annotations.
-- In some cases, you're able to mechanically propagate warnings through your code without issues. Sometimes this results in much of your public API being annotated with `RequiresUnreferencedCode`, which is the right thing to do if the library indeed behaves in ways that can't be understood statically by the trim analysis.
-- In other cases, you might discover that your code uses patterns that can't be expressed in terms of the `DynamicallyAccessedMembers` attributes, even if it only uses reflection to operate on statically known types. In these cases, you may need to reorganize some of your code to make it follow an analyzable pattern.
-- Sometimes the existing design of an API renders it mostly trim-incompatible, and you may need to find other ways to accomplish what it's doing. A common example is reflection-based serializers. In these cases, consider adopting other technology like source generators to produce code that is more easily statically analyzed.
+* ***Avoid*** reflection when possible. When using reflection, minimize reflection scope so that it's reachable only from a small part of the library.
+* Annotate code with `DynamicallyAccessedMembers` to statically express the trimming requirements when possible.
+* Consider reorganizing code to make it follow an analyzable pattern that can be annotated with `DynamicallyAccessedMembers`
+* When code is incompatible with trimming, annotate it with `RequiresUnreferencedCode` and propagate this annotation to callers until the relevant public APIs are annotated.
+* Avoid using code that uses reflection in a way not understood by the static analysis. For example, reflection in static constructors should be avoided. Using statically unanalyzable reflection in static constructors result in the warning propagating to all members of the class.
+* Avoid annotating virtual methods or interface methods. Annotating virtual or interface methods requires all overrides to have matching annotations.
+* If an API is mostly trim-incompatible, alternative coding approaches to the API may need to be considered. A common example is reflection-based serializers. In these cases, consider adopting other technology like source generators to produce code that is more easily statically analyzed. For example, see [How to use source generation in System.Text.Json](/dotnet/standard/serialization/system-text-json/source-generation)
## Resolve warnings for non-analyzable patterns
-It's better to resolve warnings by expressing the intent of your code using `RequiresUnreferencedCode` and `DynamicallyAccessedMembers` when possible. However, in some cases, you may be interested in enabling trimming of a library that uses patterns that can't be expressed with those attributes, or without refactoring existing code. This section describes some advanced ways to resolve trim analysis warnings.
+It's better to resolve warnings by expressing the intent of your code using `[RequiresUnreferencedCode]` and `DynamicallyAccessedMembers` when possible. However, in some cases, you may be interested in enabling trimming of a library that uses patterns that can't be expressed with those attributes, or without refactoring existing code. This section describes some advanced ways to resolve trim analysis warnings.
> [!WARNING]
-> These techniques might break your code if used incorrectly.
+> These techniques might change the behavior or your code or result in run time exceptions if used incorrectly.
### UnconditionalSuppressMessage
-If the intent of your code can't be expressed with the annotations, but you know that the warning doesn't represent a real issue at run time, you can suppress the warnings using . This is similar to `SuppressMessageAttribute`, but it's persisted in IL and respected during trim analysis.
+Consider code that:
+
+* The intent can't be expressed with the annotations.
+* Generates a warning but doesn't represent a real issue at run time.
+
+The warnings can be suppressed . This is similar to `SuppressMessageAttribute`, but it's persisted in IL and respected during trim analysis.
> [!WARNING]
-> When suppressing warnings, you are responsible for guaranteeing the trim compatibility of your code based on invariants that you know to be true by inspection. Be careful with these annotations, because if they are incorrect, or if invariants of your code change, they might end up hiding real issues.
+> When suppressing warnings, you are responsible for guaranteeing the trim compatibility of the code based on invariants that you know to be true by inspection and testing. Use caution with these annotations, because if they are incorrect, or if invariants of your code change, they might end up hiding incorrect code.
For example:
-```csharp
-class TypeCollection
-{
- Type[] types;
-
- // Ensure that only types with preserved constructors are stored in the array
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
- public Type this[int i]
- {
- // warning IL2063: TypeCollection.Item.get: Value returned from method 'TypeCollection.Item.get'
- // can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements.
- get => types[i];
- set => types[i] = value;
- }
-}
-
-class TypeCreator
-{
- TypeCollection types;
-
- public void CreateType(int i)
- {
- types[i] = typeof(TypeWithConstructor);
- Activator.CreateInstance(types[i]); // No warning!
- }
-}
-
-class TypeWithConstructor
-{
-}
-```
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_AD1":::
-Here, the indexer property has been annotated so that the returned `Type` meets the requirements of `CreateInstance`. This already ensures that the `TypeWithConstructor` constructor is kept, and that the call to `CreateInstance` doesn't warn. Furthermore, the indexer setter annotation ensures that any types stored in the `Type[]` have a constructor. However, the analysis isn't able to see this, and still produces a warning for the getter, because it doesn't know that the returned type has its constructor preserved.
+In the preceding code, the indexer property has been annotated so that the returned `Type` meets the requirements of `CreateInstance`. This ensures that the `TypeWithConstructor` constructor is kept, and that the call to `CreateInstance` doesn't warn. The indexer setter annotation ensures that any types stored in the `Type[]` have a constructor. However, the analysis isn't able to see this and produces a warning for the getter, because it doesn't know that the returned type has its constructor preserved.
-If you're sure that the requirements are met, you can silence this warning by adding `UnconditionalSuppressMessage` to the getter:
+If you're sure that the requirements are met, you can silence this warning by adding [`[UnconditionalSuppressMessage]`](xref:System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute) to the getter:
-```csharp
-[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
-public Type this[int i]
-{
- [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
- Justification = "The list only contains types stored through the annotated setter.")]
- get => types[i];
- set => types[i] = value;
-}
-```
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_AD2" highlight="9-10":::
-It's important to underline that it's only valid to suppress a warning if there are annotations or code that ensure the reflected-on members are visible targets of reflection. It isn't sufficient that the member was simply a target of a call, field or property access. It may appear to be the case sometimes but such code is bound to break eventually as more trimming optimizations are added. Properties, fields, and methods that aren't visible targets of reflection could be inlined, have their names removed, get moved to different types, or otherwise optimized in ways that break reflecting on them. When suppressing a warning, it's only permissible to reflect on targets that were visible targets of reflection to the trimming analyzer elsewhere.
-
-```csharp
-[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
- // Invalid justification and suppression: property being non-reflectively
- // used by the app doesn't guarantee that the property will be available
- // for reflection. Properties that are not visible targets of reflection
- // are already optimized away with Native AOT trimming and may be
- // optimized away for non-native deployment in the future as well.
- Justification = "*INVALID* Only need to serialize properties that are used by the app. *INVALID*")]
-public string Serialize(object o)
-{
- StringBuilder sb = new StringBuilder();
- foreach (var property in o.GetType().GetProperties())
- {
- AppendProperty(sb, property, o);
- }
- return sb.ToString();
-}
-```
+It's important to underline that it's only valid to suppress a warning if there are annotations or code that ensure the reflected-on members are visible targets of reflection. It isn't sufficient that the member was a target of a call, field or property access. It may appear to be the case sometimes but such code is bound to break eventually as more trimming optimizations are added. Properties, fields, and methods that aren't visible targets of reflection could be inlined, have their names removed, get moved to different types, or otherwise optimized in ways that break reflecting on them. When suppressing a warning, it's only permissible to reflect on targets that were visible targets of reflection to the trimming analyzer elsewhere.
+
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_AD3" highlight="6-8":::
### DynamicDependency
-This attribute can be used to indicate that a member has a dynamic dependency on other members. This results in the referenced members being kept whenever the member with the attribute is kept, but doesn't silence warnings on its own. Unlike the other attributes, which inform the trim analysis about the reflection behavior of your code, `DynamicDependency` only keeps other members. This can be used together with `UnconditionalSuppressMessageAttribute` to fix some analysis warnings.
+The [`[DynamicDependency]`](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) attribute can be used to indicate that a member has a dynamic dependency on other members. This results in the referenced members being kept whenever the member with the attribute is kept, but doesn't silence warnings on its own. Unlike the other attributes, which inform the trim analysis about the reflection behavior of the code, `[DynamicDependency]` only keeps other members. This can be used together with [`[UnconditionalSuppressMessage]`](xref:System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute) to fix some analysis warnings.
> [!WARNING]
-> Use `DynamicDependencyAttribute` only as a last resort when the other approaches aren't viable. It is preferable to express the reflection behavior of your code using `RequiresUnreferencedCodeAttribute` or `DynamicallyAccessedMembersAttribute`.
-
-```csharp
-[DynamicDependency("Helper", "MyType", "MyAssembly")]
-static void RunHelper()
-{
- var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
- helper.Invoke(null, null);
-}
-```
+> Use `[DynamicDependency]` attribute only as a last resort when the other approaches aren't viable. It is preferable to express the reflection behavior using [`[RequiresUnreferencedCode]`](xref:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute) or [`[DynamicallyAccessedMembers]`](xref:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute).
+
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_AD4" highlight="1":::
Without `DynamicDependency`, trimming might remove `Helper` from `MyAssembly` or remove `MyAssembly` completely if it's not referenced elsewhere, producing a warning that indicates a possible failure at run time. The attribute ensures that `Helper` is kept.
The attribute specifies the members to keep via a `string` or via `DynamicallyAccessedMemberTypes`. The type and assembly are either implicit in the attribute context, or explicitly specified in the attribute (by `Type`, or by `string`s for the type and assembly name).
-The type and member strings use a variation of the C# documentation comment ID string [format](/dotnet/csharp/language-reference/language-specification/documentation-comments#id-string-format), without the member prefix. The member string shouldn't include the name of the declaring type, and may omit parameters to keep all members of the specified name. Some examples of the format follow:
-
-```csharp
-[DynamicDependency("Method()")]
-[DynamicDependency("Method(System,Boolean,System.String)")]
-[DynamicDependency("MethodOnDifferentType()", typeof(ContainingType))]
-[DynamicDependency("MemberName")]
-[DynamicDependency("MemberOnUnreferencedAssembly", "ContainingType", "UnreferencedAssembly")]
-[DynamicDependency("MemberName", "Namespace.ContainingType.NestedType", "Assembly")]
-// generics
-[DynamicDependency("GenericMethodName``1")]
-[DynamicDependency("GenericMethod``2(``0,``1)")]
-[DynamicDependency("MethodWithGenericParameterTypes(System.Collections.Generic.List{System.String})")]
-[DynamicDependency("MethodOnGenericType(`0)", "GenericType`1", "UnreferencedAssembly")]
-[DynamicDependency("MethodOnGenericType(`0)", typeof(GenericType<>))]
-```
+The type and member strings use a variation of the C# documentation comment ID string [format](/dotnet/csharp/language-reference/language-specification/documentation-comments#id-string-format), without the member prefix. The member string shouldn't include the name of the declaring type, and may omit parameters to keep all members of the specified name. Some examples of the format are shown in the following code:
+
+:::code language="csharp" source="~/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs" id="snippet_AD5":::
-This attribute is designed to be used in cases where a method contains reflection patterns that can't be analyzed even with the help of `DynamicallyAccessedMembersAttribute`.
+The `[DynamicDependency]` attribute is designed to be used in cases where a method contains reflection patterns that can't be analyzed even with the help of `DynamicallyAccessedMembersAttribute`.
diff --git a/docs/core/deploying/trimming/snippets/ConsoleApp1/ConsoleApp1.csproj b/docs/core/deploying/trimming/snippets/ConsoleApp1/ConsoleApp1.csproj
new file mode 100644
index 0000000000000..55fd487879304
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/ConsoleApp1/ConsoleApp1.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net8.0
+ true
+
+
+
+
+
+
+
+
diff --git a/docs/core/deploying/trimming/snippets/ConsoleApp1/Program.cs b/docs/core/deploying/trimming/snippets/ConsoleApp1/Program.cs
new file mode 100644
index 0000000000000..b10ad00392688
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/ConsoleApp1/Program.cs
@@ -0,0 +1,4 @@
+using System;
+using MyLibrary1;
+
+Console.WriteLine(MyLib.MyClass.getMax(1, 2));
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs b/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs
new file mode 100644
index 0000000000000..412cedaccc435
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/Class1.cs
@@ -0,0 +1,220 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Text;
+
+namespace MyLibrary1;
+
+//
+public class MyLibrary
+{
+ public static void MyMethod()
+ {
+ // warning IL2026 :
+ // MyLibrary.MyMethod: Using 'MyLibrary.DynamicBehavior'
+ // which has [RequiresUnreferencedCode] can break functionality
+ // when trimming app code.
+ DynamicBehavior();
+ }
+
+ [RequiresUnreferencedCode(
+ "DynamicBehavior is incompatible with trimming.")]
+ static void DynamicBehavior()
+ {
+ }
+}
+//
+
+//
+public class MyLibrary3
+{
+ static void UseMethods(Type type)
+ {
+ // warning IL2070: MyLibrary.UseMethods(Type): 'this' argument does not satisfy
+ // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
+ // 'System.Type.GetMethods()'.
+ // The parameter 't' of method 'MyLibrary.UseMethods(Type)' doesn't have
+ // matching annotations.
+ foreach (var method in type.GetMethods())
+ {
+ // ...
+ }
+ }
+}
+//
+
+public class MyLibrary4
+{
+ //
+ static void UseMethods(
+ // State the requirement in the UseMethods parameter.
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
+ {
+ // ...
+ }
+ //
+}
+
+public class MyLibrary5
+{
+ private static void UseMethods(object type) => throw new NotImplementedException();
+
+ //
+ static Type type;
+ static void UseMethodsHelper()
+ {
+ // warning IL2077: MyLibrary.UseMethodsHelper(Type): 'type' argument does not satisfy
+ // 'DynamicallyAccessedMemberTypes.PublicMethods' in call to
+ // 'MyLibrary.UseMethods(Type)'.
+ // The field 'System.Type MyLibrary::type' does not have matching annotations.
+ UseMethods(type);
+ }
+ //
+}
+
+
+public class MyLibrary7
+{
+ //
+ class TypeCollection
+ {
+ Type[] types;
+
+ // Ensure that only types with preserved constructors are stored in the array
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ public Type this[int i]
+ {
+ // warning IL2063: TypeCollection.Item.get: Value returned from method
+ // 'TypeCollection.Item.get' can't be statically determined and may not meet
+ // 'DynamicallyAccessedMembersAttribute' requirements.
+ get => types[i];
+ set => types[i] = value;
+ }
+ }
+
+ class TypeCreator
+ {
+ TypeCollection types;
+
+ public void CreateType(int i)
+ {
+ types[i] = typeof(TypeWithConstructor);
+ Activator.CreateInstance(types[i]); // No warning!
+ }
+ }
+
+ class TypeWithConstructor
+ {
+ }
+ //
+}
+
+public class MyLibrary8
+{
+ //
+ class TypeCollection
+ {
+ Type[] types;
+
+ // Ensure that only types with preserved constructors are stored in the array
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
+ public Type this[int i]
+ {
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
+ Justification = "The list only contains types stored through the annotated setter.")]
+ get => types[i];
+ set => types[i] = value;
+ }
+ }
+
+ class TypeCreator
+ {
+ TypeCollection types;
+
+ public void CreateType(int i)
+ {
+ types[i] = typeof(TypeWithConstructor);
+ Activator.CreateInstance(types[i]); // No warning!
+ }
+ }
+
+ class TypeWithConstructor
+ {
+ }
+ //
+}
+
+public class MyLibrary11
+{
+ //
+ // Invalid justification and suppression: property being non-reflectively
+ // used by the app doesn't guarantee that the property will be available
+ // for reflection. Properties that are not visible targets of reflection
+ // are already optimized away with Native AOT trimming and may be
+ // optimized away for non-native deployment in the future as well.
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2063",
+ Justification = "*INVALID* Only need to serialize properties that are used by"
+ + "the app. *INVALID*")]
+ public string Serialize(object o)
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (var property in o.GetType().GetProperties())
+ {
+ AppendProperty(sb, property, o);
+ }
+ return sb.ToString();
+ }
+ //
+ private void AppendProperty(StringBuilder sb, PropertyInfo property, object o) => throw new NotImplementedException();
+}
+
+public class MyLibrary12
+{
+ //
+ [DynamicDependency("Helper", "MyType", "MyAssembly")]
+ static void RunHelper()
+ {
+ var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
+ helper.Invoke(null, null);
+ }
+ //
+}
+
+public class MyLib
+{
+ public static class MyClass
+ {
+ public static int getMax(int a, int b)
+ {
+ return a > b ? a : b;
+ }
+ }
+
+}
+public class MyLibrary99
+{
+ internal static Type type;
+}
+public class MyLibrary22
+{
+ //
+ [DynamicDependency("MyMethod()")]
+ [DynamicDependency("MyMethod(System,Boolean,System.String)")]
+ [DynamicDependency("MethodOnDifferentType()", typeof(ContainingType))]
+ [DynamicDependency("MemberName")]
+ [DynamicDependency("MemberOnUnreferencedAssembly", "ContainingType"
+ , "UnreferencedAssembly")]
+ [DynamicDependency("MemberName", "Namespace.ContainingType.NestedType", "Assembly")]
+ // generics
+ [DynamicDependency("GenericMethodName``1")]
+ [DynamicDependency("GenericMethod``2(``0,``1)")]
+ [DynamicDependency(
+ "MethodWithGenericParameterTypes(System.Collections.Generic.List{System.String})")]
+ [DynamicDependency("MethodOnGenericType(`0)", "GenericType`1", "UnreferencedAssembly")]
+ [DynamicDependency("MethodOnGenericType(`0)", typeof(GenericType<>))]
+ //
+ static void RunHelper()
+ {
+ var helper = Assembly.Load("MyAssembly").GetType("MyType").GetMethod("Helper");
+ helper.Invoke(null, null);
+ }
+}
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/Class2.cs b/docs/core/deploying/trimming/snippets/MyLibrary/Class2.cs
new file mode 100644
index 0000000000000..c2c12d000dbdd
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/Class2.cs
@@ -0,0 +1,22 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace MyLibrary2;
+class Class2
+{
+ //
+ public class MyLibrary
+ {
+ [RequiresUnreferencedCode("Calls DynamicBehavior.")]
+ public static void MyMethod()
+ {
+ DynamicBehavior();
+ }
+
+ [RequiresUnreferencedCode(
+ "DynamicBehavior is incompatible with trimming.")]
+ static void DynamicBehavior()
+ {
+ }
+ }
+ //
+}
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/Class3.cs b/docs/core/deploying/trimming/snippets/MyLibrary/Class3.cs
new file mode 100644
index 0000000000000..5df2c82c91084
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/Class3.cs
@@ -0,0 +1,36 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace MyLibrary3;
+class Class3
+{
+ //
+ public class MyLibrary
+ {
+ internal static Type type;
+
+ [RequiresUnreferencedCode("Calls DynamicBehavior.")]
+ public static void MyMethod()
+ {
+ DynamicBehavior();
+ }
+
+ [RequiresUnreferencedCode(
+ "DynamicBehavior is incompatible with trimming.")]
+ static void DynamicBehavior()
+ {
+ }
+ }
+
+ public class MyLibrary6
+ {
+ //
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
+ static Type type;
+
+ static void UseMethodsHelper()
+ {
+ MyLibrary.type = typeof(System.Tuple);
+ }
+ //
+ }
+}
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/ContainingType.cs b/docs/core/deploying/trimming/snippets/MyLibrary/ContainingType.cs
new file mode 100644
index 0000000000000..c6ffe33fd9e6f
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/ContainingType.cs
@@ -0,0 +1,3 @@
+internal class ContainingType
+{
+}
\ No newline at end of file
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/GenericType.cs b/docs/core/deploying/trimming/snippets/MyLibrary/GenericType.cs
new file mode 100644
index 0000000000000..4cb718a654553
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/GenericType.cs
@@ -0,0 +1,3 @@
+internal class GenericType
+{
+}
\ No newline at end of file
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj b/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj
new file mode 100644
index 0000000000000..ee93c65ef2ecf
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj
@@ -0,0 +1,8 @@
+
+
+
+ net8.0
+ enable
+
+
+
\ No newline at end of file
diff --git a/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj.xml b/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj.xml
new file mode 100644
index 0000000000000..b282411e74e87
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyLibrary/MyLibrary.csproj.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+ true
+
+
+
+
+
diff --git a/docs/core/deploying/trimming/snippets/MyTestLib6app/MyTestLib6app.csproj b/docs/core/deploying/trimming/snippets/MyTestLib6app/MyTestLib6app.csproj
new file mode 100644
index 0000000000000..b4a17d4f20234
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyTestLib6app/MyTestLib6app.csproj
@@ -0,0 +1,11 @@
+
+
+
+ Exe
+ net6.0
+ true
+
+ link
+
+
+
\ No newline at end of file
diff --git a/docs/core/deploying/trimming/snippets/MyTestLib6app/Program.cs b/docs/core/deploying/trimming/snippets/MyTestLib6app/Program.cs
new file mode 100644
index 0000000000000..f2b11fd28b1c7
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyTestLib6app/Program.cs
@@ -0,0 +1,2 @@
+// See https://aka.ms/new-console-template for more information
+System.Console.WriteLine("Hello, World!");
diff --git a/docs/core/deploying/trimming/snippets/MyTestLib6app/XMLFile1.xml b/docs/core/deploying/trimming/snippets/MyTestLib6app/XMLFile1.xml
new file mode 100644
index 0000000000000..4e02e5d44ab0b
--- /dev/null
+++ b/docs/core/deploying/trimming/snippets/MyTestLib6app/XMLFile1.xml
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net6.0
+ true
+
+ link
+
+
+
+
+
+
+
+
+