Trimming und Einschränkungen durch Reflection
Behandeln Sie Trimming-Warnungen, verwenden Sie [DynamicallyAccessedMembers] und ersetzen Sie Reflection zur Laufzeit durch quellcodegenerierte Alternativen.
Trimming und Einschränkungen durch Reflection ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.
Was ist IL-Trimming
IL-Trimming ist eine Optimierung zur Veröffentlichungszeit, bei der nicht verwendete Typen, Methoden und Assemblys aus der Ausgabe entfernt werden. Das Ergebnis ist eine kleinere ausführbare Datei. Für Native AOT und eigenständige Veröffentlichungen als Einzelfdatei ist IL-Trimming automatisch aktiviert.
Trimming aktivieren
Aktivieren Sie Trimming in der Projektdatei. Sie können den Trimming-Modus von partial (nur nicht verwendete Assemblys trimmen) bis full (nicht verwendete Member innerhalb von Assemblys trimmen) steuern.
// .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-containedFunktionsweise des Trimmers
Der Trimmer führt eine Erreichbarkeitsanalyse ausgehend von Einstiegspunkten durch (Main, registrierte Dienste, Attribute). Code, der von diesen Wurzeln aus nicht erreichbar ist, wird entfernt.
// 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 runtimeReflection und Trimming
Reflection ist die wichtigste Ursache für Inkompatibilitäten mit Trimming. Der Trimmer kann nicht statisch analysieren, auf welche Typen Sie über Type.GetType() oder Activator.CreateInstance() zugreifen werden.
// 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 awayDynamicDependency-Attribut
[DynamicDependency] weist den Trimmer an, bestimmte Member beizubehalten, von denen Sie wissen, dass sie über Reflection aufgerufen werden.
// 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() { }DynamicallyAccessedMembers-Annotation
[DynamicallyAccessedMembers] annotiert Parameter, Felder und Eigenschaften, um dem Trimmer mitzuteilen, auf welche Member eines Type zur Laufzeit per Reflection zugegriffen wird.
// 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
Kennzeichnen Sie Methoden, die grundsätzlich auf Reflection angewiesen sind, mit [RequiresUnreferencedCode]. Dadurch erhalten Aufrufer Warnungen, sodass sie wissen, dass der Code beim Trimming möglicherweise fehlschlägt.
// 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 expectTrim-sichere Alternativen
Für viele Reflection-lastige Muster gibt es in modernem .NET trim-sichere Alternativen. Bevorzugen Sie diese, um Trimming-Probleme zu vermeiden.
// 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+)XML-Trim-Deskriptor
Verwenden Sie einen XML-Root-Deskriptor, um ganze Namespaces oder Assemblys beizubehalten, die der Trimmer nicht analysieren kann – beispielsweise Reflection-lastige Bibliotheken von Drittanbietern.
<!-- 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>Praxisbeispiel: Trimming-Warnungen beheben
Ein praxisnaher Workflow zum Beheben von Trimmer-Warnungen in einer realen Codebasis.
// 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) { /* ... */ }Kurzer Check
Was teilt das Attribut [DynamicallyAccessedMembers] dem IL-Trimmer mit?
Zusammenfassung: Einschränkungen von Trimming und Reflection
Wichtigste Erkenntnisse:
- Trimming entfernt zur Veröffentlichungszeit nicht erreichbaren Code – Reflection beeinträchtigt diese Analyse
- Aktivieren Sie Trimming mit
<PublishTrimmed>true</PublishTrimmed>; verwenden SieTrimMode=fullfür maximale Einsparungen [DynamicDependency]: bewahrt bestimmte Member explizit für Reflection auf[DynamicallyAccessedMembers]: annotiertType-Parameter, damit der Trimmer weiß, was beibehalten werden muss[RequiresUnreferencedCode]: warnt Aufrufer, dass eine Methode nicht trim-sicher ist- Bevorzugen Sie Quellcodegeneratoren und Schnittstellen gegenüber Reflection-lastigen Mustern
Häufig gestellte Fragen
Ist die Lektion „Trimming und Einschränkungen durch Reflection“ kostenlos?
Ja — der vollständige Text von „Trimming und Einschränkungen durch Reflection“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Trimming und Einschränkungen durch Reflection“?
Behandeln Sie Trimming-Warnungen, verwenden Sie [DynamicallyAccessedMembers] und ersetzen Sie Reflection zur Laufzeit durch quellcodegenerierte Alternativen. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um C# Academy zu starten?
Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Trimming und Einschränkungen durch Reflection“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?
Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Native-AOT-Kompilierung
- Trimming und Einschränkungen durch Reflection
- ReadyToRun und tiered Compilation
- Benchmarking mit BenchmarkDotNet