If reflection works in development but fails in a release build, first find out whether the build renamed something or removed it. Obfuscation can change names that your code looks up; trimming can remove members the build cannot see are needed. The symptoms can look alike, but the fixes are different.
How can you tell whether obfuscation or trimming caused the failure?
Reproduce the problem with the release target framework, publish settings, obfuscator version, and configuration. When practical, publish four variants and compare the same reflection-dependent behavior:
As an Amazon Associate I earn from qualifying purchases.
| Build variant | What a failure points toward |
|---|---|
| Neither trimmed nor obfuscated | The issue may be in the reflection code, input name, runtime, or another release-only setting—not either transformation by itself. |
| Trimmed only | The trimmer may have removed a type or member that is reached only through reflection. |
| Obfuscated only | A type or member name used by reflection may have changed, or an obfuscator rule may have altered the relevant entity. |
| Trimmed and obfuscated | Either transformation, or their interaction, may be responsible. Compare against the single-transformation builds before changing rules. |
This comparison is a diagnostic method, not a guarantee that every project can produce all four variants. If you cannot separate the transformations, inspect the obfuscator’s rename map or logs and address trim warnings; those provide evidence about different failure modes.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Identify what reflection expects
Write down the exact operation that fails: resolving a type by string, finding a method or property by name, creating a constructor, reading an attribute, or enumerating members from a Type. Then check where the expected name or member comes from. It may be embedded in a string, a configuration file, a serialized contract, a plug-in convention, or a UI-binding name that the compiler and build tools cannot see as a direct code reference.
#1 Best Overall
A missing name after obfuscation suggests renaming. A missing type or member after trimming suggests removal. Confirm which build variant first reproduces the failure rather than inferring the cause from an exception message alone.
What should you change when obfuscation renamed a reflected name?
When the failure occurs only in the obfuscated build, inspect the tool’s rename map or diagnostics for the specific type or member that reflection expects. Then use a supported mapping or add the narrowest exclusion that preserves the external name contract. Avoid disabling obfuscation for an entire assembly when only one reflected member needs a stable name.
Use ObfuscationAttribute as tool guidance
ObfuscationAttribute is metadata for an obfuscation tool to interpret; it is not an obfuscation engine or a runtime switch. Microsoft Learn states: “Applying this attribute does not automatically obfuscate the code entity to which you apply it.” Microsoft also does not guarantee that a particular tool follows the recommended semantics.
Free tools Windows power users keep installed
One-click scans. No signup required.
using System.Reflection;
[Obfuscation(Exclude = true, ApplyToMembers = true)]
public class PluginContract
{
public void Initialize() { }
}
This example asks a compatible tool to exclude the type and, with ApplyToMembers = true, its members. If only one member is looked up by an external name, consider annotating or excluding only that member instead. Confirm the attribute’s scope and behavior in the obfuscator you actually use; a configuration rule may be clearer or more reliable for a given tool.
Rank #3
Check what the tool’s exclusion really covers
Exclusions are tool-specific. The Dotfuscator Professional User Guide 4.41 describes configurable exclusions for renaming and control-flow obfuscation; that does not establish how every obfuscator interprets attributes or rules. For your selected tool and version, verify:
- Whether you can exclude a type name, an individual member, or both.
- Whether a type-level exclusion also affects its members, and whether it applies to renaming, control flow, or other transformations.
- Whether the tool produces a rename map or diagnostics that let you confirm the expected name stayed stable.
- Whether the tool supports your project’s target runtime and build configuration.
After changing a rule, inspect the resulting map or diagnostics and run the affected reflection path against the final obfuscated artifact. Do not assume that an attribute was honored just because the project compiled.
What should you change when trimming removed reflected members?
Trimming removes code that static analysis cannot determine is needed. Reflection can make a dependency invisible to that analysis. Microsoft’s recommended order is to remove reflection where practical, express statically knowable requirements with DynamicallyAccessedMembers, and use RequiresUnreferencedCode when behavior is genuinely dynamic and cannot be analyzed.
Annotate known types with the minimum member set
When the type is known or can flow through an analyzable Type value, annotate that value with the member category the reflection operation needs. For example, if code enumerates public methods, declare that requirement:
using System;
using System.Diagnostics.CodeAnalysis;
static void ListPublicMethods(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
Type type)
{
foreach (var method in type.GetMethods())
{
Console.WriteLine(method.Name);
}
}
ListPublicMethods(typeof(MyPlugin));
The annotation is a contract that the trimmer checks. It must flow through the relevant callers and values; adding it to one method does not make arbitrary reflection safe. Select the narrowest member category that covers the operation. For instance, creating an instance with a public parameterless constructor calls for PublicParameterlessConstructor, not preservation of every member. Broad use of DynamicallyAccessedMemberTypes.All can retain more code and cause additional analysis warnings without solving unrelated dynamic-name problems.
Mark genuinely dynamic APIs with RequiresUnreferencedCode
If an API resolves arbitrary type names that are only known at runtime, the trimmer may have no analyzable way to determine what to retain. Make that limitation visible to callers:
using System.Diagnostics.CodeAnalysis;
[RequiresUnreferencedCode(
"Resolves type names that cannot be analyzed at build time.")]
static Type? ResolveByName(string name) => Type.GetType(name);
RequiresUnreferencedCode communicates a warning contract; it does not preserve the type or members by itself. The warning propagates to callers, which must address the risk, change the design, or deliberately handle a verified-safe case. Prefer a registry or another design with statically visible type references when it fits the application.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsReserve DynamicDependency and suppressions for cases annotations cannot express
Microsoft describes DynamicDependency as a last resort when preferred annotations cannot express the preservation need. It can preserve named members or member sets, but it does not tell the analyzer how the reflection behavior flows and does not silence trim warnings on its own. Suppress a warning only after verifying that the required members are preserved, keep the suppression narrowly scoped, and test the trimmed application.
How should you test the release artifact?
- Reproduce the release conditions. Match the target framework, publish settings, obfuscator version, and configuration as closely as possible.
- Separate the transformations. Compare untrimmed/unobfuscated, trimmed-only, obfuscated-only, and combined builds where practical.
- Resolve trim warnings. Remove avoidable reflection, add and propagate appropriate annotations, or mark truly non-analyzable APIs with
RequiresUnreferencedCode. Do not treat a clean build as proof that every dynamic path is safe. - Verify obfuscator behavior. Inspect rename maps or diagnostics and confirm that the rule or attribute preserves the names the application expects.
- Exercise real contracts in the final output. Test reflection lookups, serialization, binding, plug-in loading, and external name conventions that the application uses against the published and obfuscated artifact.
Microsoft’s trimming guidance recommends thorough testing after resolving warnings. A debug run or successful publish does not establish that reflection-dependent behavior works in the transformed release output.
Quick Recap
Which fix should you choose?
| Evidence | Likely cause | First fix to try |
|---|---|---|
| Failure appears in obfuscated-only output; a looked-up name changed in the rename map. | Obfuscation changed a name reflection or an external contract depends on. | Add a narrow, tool-supported exclusion or mapping, then confirm its scope in the output. |
| Failure appears in trimmed-only output; trim warnings or analysis point to reflection-dependent members. | Trimming removed members that static analysis could not see were needed. | Use the narrowest applicable DynamicallyAccessedMembers annotation and propagate it through callers. |
| Only arbitrary runtime names or otherwise non-analyzable behavior is involved. | The trimmer cannot determine the required types or members from the code flow. | Prefer a statically visible design; otherwise expose the limitation with RequiresUnreferencedCode and handle the warning contract. |
| Only the combined build fails. | The transformations may interact, or the build configuration may differ. | Compare both single-transformation builds, then inspect the final artifact and tool diagnostics before changing preservation rules. |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




