The Obfuscate public names option applies to .exe files
and not to .dll files. That is not an arbitrary limitation — it follows
from what a public member actually is.
Visibility is a contract
Private and internal members are invisible outside the assembly. Nothing else can bind to them, so renaming them cannot break anyone. They are always obfuscated.
Public members are the opposite. Marking something public is a statement
that other code may depend on it. Renaming a public member is therefore a breaking
change to everyone compiled against it.
Why an application is different from a library
An executable is normally the end of the dependency chain. Nothing compiles against
your application's public types — the public keyword there is usually
habit rather than intent. Renaming those members breaks nobody, so the option is
offered and generally worth enabling.
A library exists to be referenced. Its public surface is the product. Rename
JsonSerializer.Serialize to a.b and every consumer fails to
compile, or fails at runtime with a MissingMethodException if they were
already built. So for a DLL the public surface is preserved and everything beneath it
is renamed.
This is why the option is EXE-only. It is a safe default that prevents the single most destructive mistake in this space: shipping a library nobody can consume.
What you still get on a library
A common assumption is that preserving the public surface makes obfuscating a library pointless. It does not. In a typical library the public API is a thin layer over substantially more internal code — and that internal code is where the interesting work lives.
A reader of your obfuscated library sees the entry points they were always meant to see, documented in your published API reference anyway. What they no longer get is the readable implementation behind them: your algorithms, your heuristics, your internal data structures. That is the part worth protecting, and it is fully obfuscated.
When an EXE should not have public names renamed
Leave the option off if your executable is loaded or inspected by something else:
- It hosts a plugin system. Plugins bind to interfaces your EXE exposes; renaming those breaks every plugin.
- Another assembly references it. Uncommon, but legal — and if a tool or add-in references your EXE, its public surface is a contract.
- It is a WPF or WinForms application. Test carefully. XAML resolves bindings, commands and event handlers by name at runtime, so public renaming can silently empty your UI. See obfuscation and reflection.
- It is heavily reflective. Convention-based scanning over your own types stops matching once those types are renamed.
Shipping a protected library properly
If most of your value is in a library and you want it protected without breaking consumers, the durable answer is to shape the assemblies around that goal rather than fight the rename rules.
Split the code. Put the public contract — interfaces, DTOs, the entry-point facade — in
a small MyLib.Abstractions assembly and leave it alone. Put the real
implementation in MyLib.Core, keep its types internal, and
obfuscate it fully. Consumers reference the abstractions and never touch the
implementation directly.
Where the implementation must be reachable across assembly boundaries,
InternalsVisibleTo lets you keep types internal while still granting
access to your own assemblies — you get the protection without making the surface
public.
Choosing, in short
| You are shipping | Public renaming |
|---|---|
| A standalone application (console, service, most desktop apps) | Enable it |
| A desktop app with XAML bindings | Enable, then test every view |
| An application with a plugin API | Leave it off |
| A library consumed by other developers | Not applicable — the surface is preserved |
| A NuGet package | Split abstractions from implementation |
When in doubt, obfuscate both ways and run your smoke tests against each. The cost is a couple of minutes and it settles the question for your specific application far more reliably than a general rule can.