Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Trimming and the reflection tax

โญโญโญ advancedโณ 16 min read๐Ÿ“ Lesson 237 of 265

How the linker decides what survives, why reflection breaks, and the annotations that fix it.

Trimming and the reflection tax

The trimmer (ILLink) walks the call graph from your entry points, discarding every type/member it cannot prove used. "Cannot prove used" is the operative phrase โ€” not "unused":

var t = Type.GetType(configValue)!;      // string comes from config at RUNTIME
Activator.CreateInstance(t);             // trimmer saw a string, not a type
// โ†’ every type COULD be referenced; the trimmer must assume all of them
//   or (with trimming on) assume none โ€” either way, you break or you bloat

What breaks, and the fix:

| Dynamic pattern | Why it breaks | Static replacement | |---|---|---| | Type-by-name activation | names are data, not references | explicit factory / Dictionary<string, Func<T>> | | Assembly scanning | whole-assembly reachability is unprovable | attribute + source generator emitting registrations | | Reflection-based serialization | members via string/attribute paths | source-generated serializers (System.Text.Json) | | Activator.CreateInstance<T> with unconstrained T | no ctor provable | generic constraint where T : new() or factory parameter |

The annotations that declare intent:

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
private static Type? _handlerType;   // "this Type value is meant to have public ctors kept"

[RequiresUnreferencedCode("uses Type.GetType")]
private static void LoadPlugin(string name) { ... }   // "calling me breaks trimming"

DynamicallyAccessedMembers preserves named member kinds on a Type-typed value; RequiresUnreferencedCode flags the method so callers get build warnings โ€” the trim analysis turning reflection habits into compile-time conversations.

The honest budget: a reflection-light app trims 50โ€“70% of size for free; a plugin-heavy app fights the linker at every step and should ask whether it wants trimming at all. The checkpoint's audit challenge encodes the reasoning: which operations survive, which never do.

โšก Now practice

Ready to Code
Deployment drillsTrim-safety reasoning, dynamic-features audit, and startup-cost arithmetic.
2 challenges ยท ยท ~45 min