Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de changer d’annuaire.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer d’annuaire.
Lorsque vous activez le découpage dans votre application, le Kit de développement logiciel (SDK) .NET effectue une analyse statique pour détecter les modèles de code qui ne sont peut-être pas compatibles avec le découpage. Les avertissements d'élagage signalent des problèmes potentiels susceptibles de causer des changements de comportement ou des plantages après l'élagage.
Une application qui utilise le découpage ne doit pas produire d’avertissements de découpage. S’il existe des avertissements de découpage, testez soigneusement l’application après le découpage pour vous assurer qu’il n’y a aucune modification de comportement.
Cet article fournit des flux de travail pratiques pour traiter les avertissements de découpage. Pour mieux comprendre pourquoi ces avertissements se produisent et comment fonctionne le découpage, consultez Présentation de l’analyse de découpage.
Présentation des catégories d’avertissements
Les avertissements de découpage appartiennent à deux catégories principales :
Code incompatible avec le découpage - Marqué avec RequiresUnreferencedCodeAttribute. Le code ne peut fondamentalement pas être analysé (par exemple, le chargement dynamique d'assemblages ou les modèles de réflexion complexes). La méthode est marquée comme incompatible et les appelants reçoivent des avertissements.
Code avec exigences : annoté avec DynamicallyAccessedMembersAttribute. La réflexion est utilisée, mais les types sont connus au moment de la compilation. Lorsque les exigences sont satisfaites, le code devient entièrement compatible.
Flux de travail : déterminer l’approche appropriée
Lorsque vous rencontrez un avertissement de découpage, procédez comme suit :
- Éliminer la réflexion - Il s’agit toujours de la meilleure option si possible.
- Utilisez DynamicallyAccessedMembers : si les types sont connus, rendez le code compatible avec le rognage.
- Utilisez RequiresUnreferencedCode : si elle est véritablement dynamique, documentez l’incompatibilité.
- Supprimez les avertissements en dernier recours : uniquement si vous êtes certain que le code est sûr.
Approche 1 : Éliminer la réflexion
La meilleure solution consiste à éviter entièrement la réflexion lorsque cela est possible. Cela rend votre code plus rapide et entièrement compatible avec Trim.
Utiliser des génériques au moment de la compilation
Remplacez les opérations de type runtime par les paramètres génériques au moment de la compilation :
// ❌ Before: Uses reflection
void CreateAndProcess(Type type)
{
var instance = Activator.CreateInstance(type);
// Process instance...
}
// ✅ After: Uses generics
void CreateAndProcess<T>() where T : new()
{
var instance = new T();
// Process instance...
}
Utiliser des générateurs de code source
Le .NET moderne fournit des générateurs sources pour les scénarios de réflexion courants :
- Sérialisation : Utiliser la génération de source System.Text.Json au lieu des sérialiseurs basés sur la réflexion
- Configuration : utiliser le générateur de source de liaison de configuration
Pour plus d’informations, consultez Incompatibilités de découpage connues.
Approche 2 : Rendre le découpage de code compatible avec DynamiqueAccessedMembers
Lorsque la réflexion est nécessaire, mais que les types sont connus au moment de la compilation, utilisez DynamicallyAccessedMembersAttribute cette option pour rendre votre découpage de code compatible.
Étape par étape : Annoter l'utilisation de la réflexion
Prenons cet exemple qui génère un avertissement :
void PrintMethodNames(Type type)
{
// ⚠️ IL2070: 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods'
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
Étape 1 : Identifier l’opération de réflexion effectuée
Le code appelle GetMethods(), ce qui nécessite que PublicMethods soit conservé.
Étape 2 : annoter le paramètre
Ajoutez DynamicallyAccessedMembers pour indiquer au découpage ce qui est nécessaire :
void PrintMethodNames(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
// ✅ No warning - trimmer knows to preserve public methods
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
Étape 3 : Vérifier que les appelants répondent aux exigences
Lors de l’appel de cette méthode avec un type connu (typeof), l’exigence est automatiquement satisfaite :
// ✅ OK - DateTime's public methods will be preserved
PrintMethodNames(typeof(DateTime));
Étape par étape : propager les exigences via des chaînes d’appels
Lorsque les types transitent par plusieurs méthodes, vous devez propager les exigences :
void Method1()
{
Method2<DateTime>(); // ⚠️ Warning: Generic parameter needs annotation
}
void Method2<T>()
{
Type t = typeof(T);
Method3(t); // ⚠️ Warning: Argument doesn't satisfy requirements
}
void Method3(Type type)
{
var methods = type.GetMethods(); // ⚠️ Warning: Reflection usage
}
Étape 1 : Commencer à l’utilisation de la réflexion
Annoter où la réflexion est réellement utilisée :
void Method3(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
{
var methods = type.GetMethods(); // ✅ Fixed
}
Étape 2 : Propager vers le haut dans la chaîne d'appels
Effectuez un travail vers l’arrière dans la chaîne d’appels :
void Method2<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
{
Type t = typeof(T);
Method3(t); // ✅ Fixed - T is annotated
}
Étape 3 : Vérifier sur le site d’appel
void Method1()
{
Method2<DateTime>(); // ✅ Fixed - DateTime's public methods preserved
}
Pour plus d’informations sur la façon dont les exigences transitent par le code, consultez Présentation de l’analyse de découpage.
Les valeurs communes de DynamicallyAccessedMemberTypes
Choisissez le niveau d’accès minimal nécessaire :
| Type de membre | Quand utiliser |
|---|---|
PublicConstructors |
Utilisation Activator.CreateInstance() ou GetConstructor() |
PublicMethods |
Utilisation GetMethod() ou GetMethods() |
PublicFields |
Utilisation GetField() ou GetFields() |
PublicProperties |
Utilisation GetProperty() ou GetProperties() (sérialisation) |
PublicEvents |
Utilisation de GetEvent() ou GetEvents() |
Avertissement
L’utilisation de DynamicallyAccessedMemberTypes.All conserve tous les membres du type cible et tous les membres de ses types imbriqués (mais pas les dépendances transitives telles que les membres sur le type de retour d’une propriété). Cela augmente considérablement la taille de l’application. Plus important encore, les membres conservés deviennent accessibles, ce qui signifie qu’ils peuvent contenir leur propre code problématique. Par exemple, si un membre conservé appelle une méthode marquée avec RequiresUnreferencedCode, cet avertissement ne peut pas être résolu, car le membre est conservé par l’annotation de réflexion plutôt qu’un appel explicite. Utilisez les types de membres minimum requis pour éviter ces problèmes en cascade.
Approche 3 : Marquer le code comme incompatible avec RequiresUnreferencedCode
Lorsque le code ne peut pas être fondamentalement analysable, utilisez-le RequiresUnreferencedCodeAttribute pour documenter l’incompatibilité.
Quand utiliser RequiresUnreferencedCode
Utilisez cet attribut quand :
- Les types sont chargés dynamiquement : utilisation GetType() avec des chaînes déterminées par le runtime.
- Les assemblies sont chargées à l'exécution : utilisation LoadFrom(String).
- Modèles de réflexion complexes : l’utilisation de la réflexion est trop complexe pour annoter.
-
Génération de code à l'exécution : utilisation System.Reflection.Emit ou le mot clé
dynamic.
Étape par étape : Marquer les méthodes incompatibles
Étape 1 : Identifier le code réellement incompatible
Exemple de code qui ne peut pas être adapté pour être compatible avec l'élagage :
void LoadPluginByName(string pluginName)
{
// Type name comes from runtime input - trimmer cannot know what types are needed
Type pluginType = Type.GetType(pluginName);
var plugin = Activator.CreateInstance(pluginType);
// Use plugin...
}
Étape 2 : Ajouter l’attribut RequiresUnreferencedCode
[RequiresUnreferencedCode("Plugin loading by name is not compatible with trimming. Consider using compile-time plugin registration instead.")]
void LoadPluginByName(string pluginName)
{
Type pluginType = Type.GetType(pluginName);
var plugin = Activator.CreateInstance(pluginType);
// ✅ No warnings inside this method - it's marked as incompatible
}
Étape 3 : les appelants reçoivent des avertissements
void InitializePlugins()
{
// ⚠️ IL2026: Using member 'LoadPluginByName' which has 'RequiresUnreferencedCodeAttribute'
// can break functionality when trimming application code. Plugin loading by name is not
// compatible with trimming. Consider using compile-time plugin registration instead.
LoadPluginByName("MyPlugin");
}
Écriture de messages d’avertissement effectifs
Un bon RequiresUnreferencedCode message doit :
- Indiquez quelle fonctionnalité est incompatible : Soyez précis sur ce qui ne fonctionne pas avec le découpage.
- Proposer des alternatives : Guidez les développeurs vers des solutions compatibles avec l'élagage.
- Soyez concis : gardez les messages courts et actionnables.
// ❌ Not helpful
[RequiresUnreferencedCode("Uses reflection")]
// ✅ Helpful - explains problem and suggests alternative
[RequiresUnreferencedCode("Dynamic type loading is not compatible with trimming. Use generic type parameters or source generators instead.")]
Pour obtenir des conseils plus longs, ajoutez un Url paramètre :
[RequiresUnreferencedCode(
"Plugin system is not compatible with trimming. See documentation for alternatives.",
Url = "https://docs.example.com/plugin-trimming")]
Propagation de RequiresUnreferencedCode
Lorsqu’une méthode appelle une autre méthode marquée avec RequiresUnreferencedCode, vous devez généralement propager l’attribut :
class PluginSystem
{
// Use a constant for consistent messaging
const string PluginMessage = "Plugin system is not compatible with trimming. Use compile-time registration instead.";
[RequiresUnreferencedCode(PluginMessage)]
private void LoadPluginImplementation(string name)
{
// Low-level plugin loading
}
[RequiresUnreferencedCode(PluginMessage)]
public void LoadPlugin(string name)
{
LoadPluginImplementation(name); // ✅ No warning - method is also marked
}
}
Modèles et solutions courants
Modèle : méthodes de fabrique avec Activator.CreateInstance
// ❌ Before: Produces warning
object CreateInstance(Type type)
{
return Activator.CreateInstance(type);
}
// ✅ After: Trim-compatible
object CreateInstance(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
{
return Activator.CreateInstance(type);
}
Modèle : Systèmes de plug-in qui chargent des assemblys
// This pattern is fundamentally incompatible with trimming
[RequiresUnreferencedCode("Plugin loading is not compatible with trimming. Consider compile-time plugin registration using source generators.")]
void LoadPluginsFromDirectory(string directory)
{
foreach (var dll in Directory.GetFiles(directory, "*.dll"))
{
Assembly.LoadFrom(dll);
}
}
Modèle : Conteneurs d’injection de dépendances
// Complex DI containers are often incompatible
class Container
{
[RequiresUnreferencedCode("Service resolution uses complex reflection. Consider using source-generated DI or registering services explicitly.")]
public object Resolve(Type serviceType)
{
// Complex reflection to resolve dependencies
}
}
Approche 4 : Supprimer les avertissements en dernier recours
Avertissement
Supprimez uniquement les avertissements de réduction si vous êtes absolument certain que le code est sûr. Des suppressions incorrectes peuvent entraîner des échecs d’exécution après la suppression.
Utilisez UnconditionalSuppressMessageAttribute lorsque vous avez vérifié que le code est sûr pour le découpage, mais que l'outil de découpage ne peut pas le prouver statiquement.
Lorsque la suppression est appropriée
Supprimez les avertissements uniquement lorsque :
- Vous avez vérifié manuellement que tout le code requis est conservé (via
DynamicDependencyou d’autres mécanismes). - Le chemin du code n’est jamais exécuté dans des scénarios réduits.
- Vous avez soigneusement testé l’application optimisée.
Comment supprimer les avertissements
[RequiresUnreferencedCode("Uses reflection")]
void MethodWithReflection() { /* ... */ }
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
Justification = "All referenced types are manually preserved via DynamicDependency attributes")]
void CallerMethod()
{
MethodWithReflection(); // Warning suppressed
}
Importante
N’utilisez pas SuppressMessage ni #pragma warning disable pour les avertissements de limitation. Ces opérations fonctionnent uniquement pour le compilateur, mais ne sont pas conservées dans l’assembly compilé. Le trimmer fonctionne avec des assemblies compilés et n'intercepte pas ces suppressions. Utilisez toujours UnconditionalSuppressMessage.
Réduire l’étendue de suppression
Appliquez des suppressions à la plus petite étendue possible. Extrayez l’appel problématique dans une fonction locale :
void ProcessData()
{
InitializeData();
CallReflectionMethod(); // Only this call is suppressed
ProcessResults();
[UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
Justification = "Types are preserved via DynamicDependency on ProcessData method")]
void CallReflectionMethod()
{
MethodWithReflection();
}
}
Cette approche :
- Indique clairement quel appel spécifique est supprimé.
- Empêche la suppression accidentelle d’autres avertissements si le code change.
- Maintient la justification près de l'appel supprimé.
Conseils de dépannage
L'avertissement persiste après l'ajout de DynamicallyAccessedMembers
Vérifiez que vous avez annoté l’ensemble de la chaîne d’appels de l’utilisation de la réflexion à la source du Type:
- Recherchez où la réflexion est utilisée (par exemple
GetMethods()). - Annotez le paramètre de cette méthode.
- Suivez la valeur
Typeen remontant tous les appels de méthode. - Annotez chaque paramètre, champ ou paramètre de type générique dans la chaîne.
Trop d’avertissements à traiter
- Commencez par votre propre code : corrigez d’abord les avertissements dans le code que vous contrôlez.
- Utilisez
TrimmerSingleWarnpour voir les avertissements individuels des paquets. - Déterminez si le découpage est approprié pour votre application.
- Vérifiez les incompatibilités de découpage connues pour les problèmes au niveau de l’infrastructure.
Pas certain de quels DynamicallyAccessedMemberTypes à utiliser
Examinez l’API de réflexion appelée :
-
GetMethod()/GetMethods()→PublicMethods -
GetProperty()/GetProperties()→PublicProperties -
GetField()/GetFields()→PublicFields -
GetConstructor()/Activator.CreateInstance()PublicParameterlessConstructor→ ouPublicConstructors -
GetEvent()/GetEvents()→PublicEvents
Utilisez le type le plus étroit possible pour réduire la taille de l’application.
Étapes suivantes
- Présentation de l’analyse de découpage - Découvrez les concepts fondamentaux derrière les avertissements de découpage
- Préparer les bibliothèques pour le découpage - Rendre vos bibliothèques compatibles avec la suppression
- Référence d’avertissement de découpage - Informations détaillées sur les codes d’avertissement spécifiques
- Incompatibilités connues - Modèles qui ne peuvent pas être rendus compatibles avec les découpages