Limites de la réduction et de la réflexion
Gérez les avertissements liés à la réduction, utilisez [DynamicallyAccessedMembers] et remplacez la réflexion à l’exécution par des solutions générées à partir du code source.
Limites de la réduction et de la réflexion est une leçon C# Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage C# Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que la réduction IL ?
La réduction IL est une optimisation effectuée lors de la publication qui supprime les types, les méthodes et les assemblys inutilisés de la sortie. Il en résulte un exécutable plus petit. Elle est automatiquement activée pour Native AOT et les publications autonomes en fichier unique.
Activer la réduction du code
Activez la réduction du code dans le fichier de projet. Vous pouvez contrôler le mode de réduction, de partial (réduction des assemblys inutilisés uniquement) à full (réduction des membres inutilisés au sein des assemblys).
// .csproj
<PropertyGroup>
<!-- Enable trimming on publish -->
<PublishTrimmed>true</PublishTrimmed>
<!-- full: aggressive — trims unused members within assemblies
partial: conservative — only removes unused assemblies -->
<TrimMode>full</TrimMode>
<!-- Treat all trimmer warnings as errors (recommended for CI) -->
<TrimmerRootAssemblies>MyApp</TrimmerRootAssemblies>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>
// Publish:
dotnet publish -c Release -r linux-x64 --self-containedFonctionnement de l'outil de réduction
L'outil de réduction effectue une analyse d'accessibilité en partant des points d'entrée (Main, services enregistrés et attributs). Tout code inaccessible depuis ces racines est supprimé.
// Example: only MyApp.Program.Main is a root
// Code flow:
// Main → WebApplication.Run → MapGet → MyHandler
// Everything else is trimmed
// What gets KEPT:
class MyHandler { public string Handle() => "ok"; }
// What gets TRIMMED (if not reached):
class UnusedService { public void DoWork() { } }
// Problem: reflection can access UnusedService at runtime
// But the trimmer can't know that at compile time
// → UnusedService is trimmed → MissingMethodException at runtimeRéflexion et réduction du code
La réflexion est la principale source d'incompatibilités avec la réduction du code. L'outil de réduction ne peut pas analyser statiquement les types auxquels vous accéderez via Type.GetType() ou Activator.CreateInstance().
// PROBLEMATIC: type name comes from config at runtime
var typeName = config["Plugin:Type"]!;
var type = Type.GetType(typeName); // type may have been trimmed!
var instance = Activator.CreateInstance(type!); // MissingMethodException
// PROBLEMATIC: LINQ expressions with reflection
var query = dbContext.Set<T>() // T discovered via reflection
.Where(BuildExpression<T>("Name", "Alice"));
// PROBLEMATIC: attribute scanning
var handlers = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.HasCustomAttribute<HandlerAttribute>());
// All handler types may be trimmed awayAttribut DynamicDependency
[DynamicDependency] indique à l'outil de réduction de conserver certains membres précis dont vous savez qu'ils seront accessibles par réflexion.
// Keep all public constructors of MyPlugin:
[DynamicDependency(
DynamicallyAccessedMemberTypes.PublicConstructors,
typeof(MyPlugin))]
public static IPlugin CreatePlugin()
=> (IPlugin)Activator.CreateInstance(typeof(MyPlugin))!;
// Keep a specific method by name:
[DynamicDependency("ProcessOrder", typeof(OrderHandler))]
public static void Bootstrap() { }
// Keep everything on a type (use sparingly):
[DynamicDependency(
DynamicallyAccessedMemberTypes.All,
typeof(LegacyReflectionHelper))]
public static void EnsurePreserved() { }Annotation DynamicallyAccessedMembers
[DynamicallyAccessedMembers] annote les paramètres, les champs et les propriétés afin d'indiquer à l'outil de réduction quels membres d'un Type seront accessibles par réflexion au moment de l'exécution.
// Annotate a method parameter:
public void Register(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
Type serviceType)
{
// trimmer knows to preserve public constructors of whatever Type is passed
var instance = Activator.CreateInstance(serviceType)!;
}
// Annotate a generic parameter:
public static T Create<
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]
T>() where T : new()
=> new T();
// Annotate a field:
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
private Type _modelType = typeof(OrderModel);RequiresUnreferencedCode
Marquez les méthodes qui dépendent fondamentalement de la réflexion avec [RequiresUnreferencedCode]. Cela transmet des avertissements aux appelants afin qu'ils sachent que le code peut échouer lorsqu'il est réduit.
// Mark a method that uses reflection internally:
[RequiresUnreferencedCode("Uses Type.GetType() — not safe for trimming")]
public static object LoadPlugin(string typeName)
{
var type = Type.GetType(typeName)!
?? throw new InvalidOperationException($"Type {typeName} not found");
return Activator.CreateInstance(type)!;
}
// Callers will see a trimmer warning:
// IL2026: Members decorated with [RequiresUnreferencedCode]
// need their callers to be decorated with the same attribute
// or be suppressed with [UnconditionalSuppressMessage]
// The warning reminds callers to use a trim-safe alternative
// or add a DynamicDependency for the types they expectAlternatives compatibles avec la réduction
De nombreux modèles reposant fortement sur la réflexion disposent d'alternatives compatibles avec la réduction dans les versions modernes de .NET. Privilégiez ces alternatives pour éviter les problèmes liés à la réduction.
// INSTEAD OF: Type.GetType(name) + Activator.CreateInstance
// USE: registered factories or switch expressions:
public IHandler CreateHandler(string name) => name switch
{
"order" => new OrderHandler(),
"email" => new EmailHandler(),
_ => throw new ArgumentException($"Unknown: {name}")
};
// INSTEAD OF: reflection-based JSON serialization
// USE: source-generated JsonSerializerContext
// INSTEAD OF: attribute scanning via Assembly.GetTypes()
// USE: compile-time source generators
// INSTEAD OF: dynamic proxy libraries (Castle, DispatchProxy)
// USE: source-generated interceptors (.NET 8+)Descripteur XML de réduction
Utilisez un descripteur racine XML pour conserver des espaces de noms ou des assemblys entiers que l'outil de réduction ne peut pas analyser, par exemple des bibliothèques tierces reposant fortement sur la réflexion.
<!-- TrimmerRoots.xml -->
<linker>
<!-- Keep everything in LegacyLib -->
<assembly fullname="LegacyLib">
<type fullname="LegacyLib.*" preserve="all" />
</assembly>
<!-- Keep a specific type -->
<assembly fullname="MyApp">
<type fullname="MyApp.Plugins.PluginLoader" preserve="all" />
</assembly>
</linker>
// Reference in .csproj:
<ItemGroup>
<TrimmerRootDescriptor Include="TrimmerRoots.xml" />
</ItemGroup>Dans la pratique : résoudre les avertissements de réduction
Un processus pratique pour résoudre les avertissements de l'outil de réduction dans une base de code réelle.
// Step 1: enable analyzer during development
// .csproj:
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
// Step 2: build and read warnings:
// warning IL2026: 'MyMapper.MapDynamic(Type)' uses
// [RequiresUnreferencedCode] via 'Type.GetMethod'
// Step 3: fix the root cause:
// BEFORE:
public void MapDynamic(Type t)
{
var method = t.GetMethod("MapFrom")!; // IL2026
method.Invoke(null, null);
}
// AFTER: use an interface instead of reflection
public void Map(IMapper mapper) => mapper.MapFrom();
// Step 4: if fix is not feasible, add annotation and document:
[RequiresUnreferencedCode("Uses reflection: ensure target types are preserved")]
public void MapDynamic(Type t) { /* ... */ }Vérification rapide
Qu'indique l'attribut [DynamicallyAccessedMembers] à l'outil de réduction IL ?
Récapitulatif : limites de la réduction et de la réflexion
Points essentiels :
- La réduction supprime le code inaccessible au moment de la publication — la réflexion perturbe cette analyse
- Activez-la avec
<PublishTrimmed>true</PublishTrimmed>; utilisezTrimMode=fullpour réaliser les économies maximales [DynamicDependency]: conserve explicitement certains membres pour la réflexion[DynamicallyAccessedMembers]: annote les paramètres de typeTypeafin d'indiquer à l'outil de réduction ce qu'il doit conserver[RequiresUnreferencedCode]: avertit les appelants qu'une méthode n'est pas compatible avec la réduction- Privilégiez les générateurs de code source et les interfaces aux modèles reposant fortement sur la réflexion
Questions Fréquemment Posées
La leçon « Limites de la réduction et de la réflexion » est-elle gratuite ?
Oui — le texte complet de « Limites de la réduction et de la réflexion » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours C# Academy, passe à CoddyKit PRO. Le cours C# Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Limites de la réduction et de la réflexion » ?
Gérez les avertissements liés à la réduction, utilisez [DynamicallyAccessedMembers] et remplacez la réflexion à l’exécution par des solutions générées à partir du code source. Tu pratiques C# Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer C# Academy ?
Aucune expérience préalable n'est requise. C# Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.
Combien de temps prend la leçon « Limites de la réduction et de la réflexion » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon C# Academy ?
Oui. Chaque leçon C# Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Compilation Native AOT
- Limites de la réduction et de la réflexion
- ReadyToRun et compilation par niveaux
- Évaluation des performances avec BenchmarkDotNet