Diff two versions of a .NET assembly with Glass.NET
You have v1.2.0 and v1.3.0 of a DLL and need to know exactly what changed — new APIs, a silently altered method, a fresh network call — without the source and without running either build. Here is how to diff two assembly versions in Glass.NET, the free, static-only .NET decompiler.
You have v1.2.0 of a DLL and someone just handed you v1.3.0. Maybe it’s a dependency your package manager wants to bump, a hotfix a vendor emailed over, or a NuGet update with a changelog that says “bug fixes and improvements.” Before you trust it in your build, you want the honest answer to one question: what actually changed?
Not what the release notes claim. The real diff — new and removed types, a method body that was quietly rewritten, a public signature that shifted, a network call that wasn’t there last version. And you want it without the source (you don’t have it) and without running either build (you don’t trust it yet).
That’s a decompile-and-diff job, and Glass.NET is built for it. It’s free, and it is strictly static — it reads metadata and IL and reconstructs C#, and never executes the assembly it inspects. So you can diff an update you’re unsure about safely: nothing in either DLL runs. Glass has a side-by-side assembly compare with a line diff that puts the two versions next to each other and shows you the delta directly, method by method.
Why “read the changelog” isn’t enough
A changelog is a claim, not evidence. The shipped binary is the source of truth for what runs, and it diverges from the story in all the usual ways:
- The binary isn’t the repo. A
.nupkgis a compiled artifact pulled from a feed. Unless it’s a verifiably reproducible build, the DLL — not the GitHub tag — is what executes in your process. - Patch bumps hide real changes. A “bug fix” release can add a new outbound call, change how a value is validated, or alter a default. Semantic versioning is a convention, not a guarantee.
- Breaking changes slip out silently. A public method that quietly changed its signature or disappeared will break your callers at runtime even when the version number says “safe minor bump.”
Reading the delta yourself is cheap insurance, and it’s the same discipline as inspecting a NuGet package before you trust it — except now you already trusted the old version, so you only need to audit what moved.
Load both versions
Get both DLLs on disk. For a NuGet dependency they’re in the global packages cache — %USERPROFILE%\.nuget\packages\<id>\<version>\lib\<tfm>\ on Windows, ~/.nuget/packages/... elsewhere — one folder per version, so you can grab 1.2.0 and 1.3.0 side by side. For a vendor drop or your own output, just point at the two files.
Open the first assembly in Glass.NET (drag it onto the window, or File ▸ Open Assembly), then open the second. Each lists as a namespaces ▸ types ▸ members tree, with readable C# reconstructed on demand — no PDB, no Source Link, no special build required.
Run the assembly compare
Start Glass.NET’s assembly compare and pick the two versions as the old and new side. Glass decompiles both and shows a line diff of the reconstructed C#, aligned so you read the difference directly instead of flipping between two windows and trusting your memory. This is the core of the workflow: you’re reviewing the delta, not re-reading the whole library.
Work through it with specific questions.
1. What types and members appeared or disappeared?
Start at the structural level, before any method bodies. The compare surfaces types and members that are new in v1.3.0, gone from it, or changed. Ask what each one means:
- A new type — especially one that touches I/O, serialization, or process control — is the first thing to understand. A logging helper is routine; a new
*Clientor*Uploaderin a library that had no reason to talk to the network is worth a hard look. - A removed type or member is a compatibility signal. If your code referenced it, the bump breaks you — better to learn that here than from a runtime
MissingMethodException. - A new or removed field on a type you serialize can silently change on-disk or on-wire formats.
2. Did the public API surface change?
Narrow to the public surface — the contract everyone downstream depends on. Expand the public types and members on both sides and let the compare show what was added, removed, or re-signed:
// v1.2.0
public Result Process(string input)
// v1.3.0
public Result Process(string input, CancellationToken token = default)
An added optional parameter like that is usually source-compatible but not binary-compatible — callers must recompile, and a plugin loaded against the old signature will fault. A removed public method, a changed return type, or a parameter that lost its default is a harder break. The point of diffing the public surface specifically is that this is where a “minor” version bump does the damage it’s least honest about.
3. What changed inside a method body?
For a method that exists in both versions, the line diff shows exactly how its reconstructed C# changed. This is where you catch the quiet rewrite — the thing no changelog mentions:
- A validation check that got removed or loosened.
- A hardcoded constant, limit, or default that moved.
- A new branch that calls out to something — a file, a registry key, a URL.
The highest-value catch here is a new network call or process spawn that the old version didn’t have. If v1.3.0 of a date-formatting helper suddenly constructs an HttpClient and posts to a hardcoded endpoint, the diff puts those exact lines in front of you. That’s the same red-flag hunt covered in analyzing a suspicious .NET assembly — the diff just narrows it to what’s new.
4. Read the IL where the C# is ambiguous
Decompiled C# is a reconstruction, and two genuinely different builds can occasionally produce C# that looks the same, or differ only in a way the C# view rounds off — a changed overload resolution, a shift between == and object.Equals, a compiler-generated state machine that reorganized. When a method’s C# diff looks suspiciously empty or suspiciously noisy and you need certainty, switch to Glass.NET’s IL view and compare the IL directly. The IL is what actually runs, so it’s the ground truth; reading IL is the tie-breaker when the C# is ambiguous. For most methods the C# diff is plenty — reach for IL on the handful where it matters.
Use the exposure report as a quick gate
Glass.NET also produces an exposure report that summarizes how readable an assembly is. Run it on both versions as a fast triage signal: if v1.3.0 suddenly reports far less readable than v1.2.0, a previously plain dependency may have started shipping obfuscated or virtualized code — which, in an ordinary open-source-style library, is itself a reason to stop and ask why. (If you’re on the other side of that — protecting your own build — that’s what the sibling obfuscator Nebula.NET is for, and code virtualization is a Pro feature; you’d then verify the result in Glass.)
Know the limits
Diffing two versions tells you precisely what changed in the bytes you have — it doesn’t tell you whether a change is safe, only what it is; that judgment is still yours. Static reading won’t surface behaviour that only appears at runtime with specific inputs, and heavily obfuscated code may not reconstruct cleanly on either side (again, a signal in its own right). And you’re comparing two specific builds: pin your dependencies and re-diff on every upgrade, so the delta is always small enough to actually read.
That’s the whole point. Instead of re-auditing an entire library on every bump, you review only what moved — new APIs, removed members, rewritten bodies, a fresh network call — in a free, static-only tool that never runs a line of either build. Download Glass.NET, load both versions, and read the diff before you trust the new one.
Try Nebula.NET
Harden your .NET code in minutes — start with the free edition.