rea-reverse

ILSpy vs dnSpyEx vs dotPeek: Choosing a .NET Decompiler in 2026

Written for REA 6.4.0Updated 8 min read
On this page

Short answer, as of 2026-10-14: use ILSpy if you want a free, open-source decompiler that now runs natively on Windows, macOS and Linux and has a scriptable command-line twin, ilspycmd. Use dnSpyEx if you need to step through .NET code without source on Windows. Use dotPeek if you already live in JetBrains tools on Windows and want a free decompiler that can also act as a symbol server. The original dnSpy repository has been archived since 2020; dnSpyEx is the maintained continuation.

This page is for .NET developers choosing a decompiler to understand their own builds, a dependency that ships without source, or a crash in code they maintain. It covers the current versions, platforms and licences, a side-by-side decompilation of one DLL I wrote, automation, and how REA (the open-source project by morluto) records decompiler output.

Scope and fairness. All facts below come from each project’s own release pages or website, checked on 2026-10-14. I ran ILSpy’s ilspycmd 11.1 myself. dnSpyEx and dotPeek are Windows applications and this article was written on macOS, so I did not run them; what I say about them is from their official documentation, cited inline. dnSpyEx can also edit and save assemblies. This article does not cover that: use it on your own code, or code you are authorized to examine.

The three tools at a glance

ILSpy dnSpyEx dotPeek
Latest release (checked 2026-10-14) 11.1, 2026-09-23 6.6.0, 2026-06-20 2026.2.3.1, 2026-10-01
Licence MIT (open source) GPL-3.0 (open source) Free of charge, proprietary
Platforms Windows, macOS, Linux Windows (WPF) Windows (x64, x86, ARM64 downloads)
Decompile to C# Yes Yes Yes
Debugger No Yes No (can serve symbols to a debugger)
Command line ilspycmd dotnet tool dnSpy.Console export Not covered on its product page
Single-file bundles Yes Not documented in the sources I used Not documented in the sources I used

Sources: the ILSpy 11.1 and 11.0 release notes, the dnSpyEx 6.6.0 release notes and README, and JetBrains’ dotPeek page. The dotPeek version and platforms come from JetBrains’ public release feed, which lists Windows downloads only.

Where each project stands in 2026

ILSpy: now cross-platform

ILSpy 11.0 (August 2026) ported the UI from WPF to Avalonia. In the project’s words, “With this port, ILSpy is now available on Linux and Mac in addition to Windows,” and the WPF front end was removed. The 11.1 release page offers Windows installers for x64 and ARM64, a Linux zip with .deb and .rpm packages, and a macOS ARM64 zip. This answers the long-standing question of a “dnSpy alternative for Mac or Linux” for browsing and decompiling. It does not add a debugger.

11.0 also targeted agent use of ilspycmd: it added --member for decompiling a single member and --dump-table for metadata tables. The release notes say downloads of the dotnet tool exceed 70k a week and credit agents such as Claude for the growth.

Visual Studio’s own debugger decompiler is built on ILSpy. Microsoft’s decompilation page says source generation “is based on the open source ILSpy project” and has been available since Visual Studio 2019 16.5.

dnSpy and dnSpyEx

The original dnSpy/dnSpy repository on GitHub is archived, with its last push on 2020-12-20. dnSpyEx describes itself as “an unofficial continuation of the dnSpy project which is a debugger and .NET assembly editor.” Version 6.6.0 is its first stable release in two years. It moved to .NET 10, added a dnSpy-net build that uses an installed .NET 10 runtime, added a String References window, and fixed decompiler crashes for runtime-async methods.

Two points from that release page matter when you choose:

  • It is a Windows WPF application. 6.6.0 “detects when it is being run under Wine on Linux” and disables hardware acceleration to improve compatibility. That is a compatibility tweak, not a Linux or macOS build.
  • The maintainer states there “will not be an official dnSpy AI integration, or an MCP server.”

Its debugger is the reason most people pick it. The README lists debugging .NET Framework, .NET and Unity assemblies “no source code required”, breakpoints in any assembly, and locals, watch and call-stack windows.

dotPeek

JetBrains describes dotPeek as “a free-of-charge standalone tool based on ReSharper’s bundled decompiler.” It decompiles to C# or IL, can save a decompiled assembly as a Visual Studio project, and “can also perform as a symbol server” so that the Visual Studio debugger can step into decompiled library code. The current help is labelled dotPeek 2026.2. JetBrains Rider has its own integrated decompiler and can debug decompiled external code; per the Rider 2026.2 external code debugging help, stepping into external methods is not supported on ARM64 macOS. Rider is a separate, licensed IDE, and I did not test it.

Same DLL, decompiled

To see output quality I wrote a small library with a record, a LINQ chain, an async method and a switch expression, and built it in Release with the .NET 10 SDK (Demo.dll, 10,240 bytes):

public record Order(int Id, string Customer, decimal Total, bool Paid);

public static class OrderReport
{
    public static IEnumerable<string> TopCustomers(IEnumerable<Order> orders, int count) =>
        orders.Where(o => o.Paid)
              .GroupBy(o => o.Customer)
              .OrderByDescending(g => g.Sum(o => o.Total))
              .Take(count)
              .Select(g => $"{g.Key}: {g.Sum(o => o.Total):0.00}");

    public static async Task<int> CountLinesAsync(string path, CancellationToken ct = default)
    {
        using var reader = new StreamReader(path);
        var lines = 0;
        while (await reader.ReadLineAsync(ct) is not null)
            lines++;
        return lines;
    }

    public static string Describe(Order o) => o switch
    {
        { Paid: false } => "unpaid",
        { Total: > 1000m } => "large",
        _ => "normal",
    };
}

ilspycmd -t Demo.Order Demo.dll returned the record as one line, exactly like the source:

public record Order(int Id, string Customer, decimal Total, bool Paid);

ilspycmd -t Demo.OrderReport Demo.dll returned this (the using lines are omitted):

public static IEnumerable<string> TopCustomers(IEnumerable<Order> orders, int count)
{
	return from g in (from o in orders
			where o.Paid
			group o by o.Customer into g
			orderby g.Sum((Order o) => o.Total) descending
			select g).Take(count)
		select $"{g.Key}: {g.Sum((Order o) => o.Total):0.00}";
}

public static async Task<int> CountLinesAsync(string path, CancellationToken ct = default(CancellationToken))
{
	using StreamReader reader = new StreamReader(path);
	int lines = 0;
	while (await reader.ReadLineAsync(ct) != null)
	{
		lines++;
	}
	return lines;
}

public static string Describe(Order o)
{
	if ((object)o != null)
	{
		if (!o.Paid)
		{
			return "unpaid";
		}
		if (o.Total > 1000m)
		{
			return "large";
		}
	}
	return "normal";
}

What to take from this:

  • The record and the async method came back as you would write them. The compiler-generated state machine is hidden.
  • The LINQ chain is correct but rewritten in query syntax, with explicit lambda parameter types.
  • The switch expression became nested if statements with a (object)o != null check. The logic is the same, the shape is not. Pattern matching is where you should expect the biggest differences between decompilers and between versions.

That run took 0.98 s and 136 MB of peak memory for one type on an Apple M5 Pro. I cannot show dnSpyEx or dotPeek output for the same DLL because I did not run them. If you compare them yourself, keep the same build and look at these four constructs first.

Debugging your own code without its source

The common legitimate case: a crash happens inside a library you depend on, or inside an old build of your own app whose PDB is lost.

  1. Try source first. If the package has Source Link or you still have the PDB, a normal debugger shows the real source. Microsoft’s decompilation page points to Source Link for libraries and NuGet packages that enable it.
  2. Visual Studio 2022 17.7 and later automatically decompiles external .NET code when you step into it, if Just My Code is off, or when you double-click a frame in the Call Stack window. You can set breakpoints in the generated code. Microsoft lists limits: optimized builds may bind breakpoints to the wrong line, local names may be missing, and async code may decompile incompletely.
  3. dnSpyEx gives the same “debug without source” ability as a standalone Windows tool, across .NET Framework, .NET and Unity.
  4. dotPeek can act as a symbol server so that the Visual Studio debugger gets source for assemblies you have no PDB for.

ILSpy itself does not debug. ilspycmd -genpdb can generate a portable PDB from decompiled code, which a debugger can then load. I did not test that path for this article.

Automation: ilspycmd and REA

ilspycmd installs as a .NET tool:

dotnet tool install ilspycmd --version 11.1.0.9782 --tool-path ./tools
./tools/ilspycmd --version
# ilspycmd: 11.1.0.9782
# ICSharpCode.Decompiler: 11.1.0.9782

Options I used: -t <type> for one type, -m <token or doc id> for one member, -l c to list classes, -p -o <dir> for a compilable project, --bundle-entry and -d for single-file bundles (see is this EXE a .NET app?), and --disable-updatecheck for scripts. Given a NativeAOT binary or a native apphost it fails with PE file does not contain any managed metadata.

REA 6.4.0 does not run a decompiler itself. Its .NET commands read metadata and IL directly, as in .NET assembly static analysis with REA. It has two touch points for ILSpy:

  • rea doctor checks REA_ILSPY_CMD_PATH when you set it. With my install it reported ilspycmd ... (ilspycmd: 11.1.0.9782) as healthy. The path must be absolute.
  • import-managed-reconstruction (MCP tool import_managed_reconstruction) imports decompiler output and ties it to the exact method it came from. The decompiler family field accepts ilspy, dnspy, dnlib, mono-cecil or other.

I tested the full round trip on Demo.dll:

rea inspect-managed-members /abs/path/Demo.dll --json > members.json
ilspycmd --disable-updatecheck -m 0x06000017 Demo.dll > describe.cs
# import.json = { static_members: <members.json>, decompiler: {name, version, family, options},
#                 methods: [{ token, signature_sha256, normalized_il_sha256, reconstruction: {kind, language, text} }] }
rea import-managed-reconstruction import.json --json

0x06000017 is Describe. The signature and IL hashes come from the members evidence (signature.raw_sha256 and body.normalized_il_sha256). REA accepted the import with this validation block:

{ "matched_static_member": true, "exact_build_required": true,
  "canonical_observation": false, "confidence_floor": "inference" }

and the limitation “Decompiler output is imported as reconstruction evidence and analyst inference; metadata and IL observations remain canonical.” When I changed the IL hash to zeros, it refused: Reconstruction method lock 0x06000017 does not match a static managed member observation. That is the useful property: decompiled C# stays labelled as inference, and it cannot be attached to a different build by mistake.

When you do not need a decompiler

Many questions are answered by metadata alone: target framework, references, version, P/Invoke imports, or whether the file is .NET at all. This site’s .NET metadata and IL viewer shows those and the IL of each method in the browser, without uploading the file. It does not produce C#. For the framework and version questions, see check a .NET DLL’s target framework.

Which should you pick?

  • On macOS or Linux, or in CI and agent workflows: ILSpy and ilspycmd.
  • On Windows and you must step through code without source: Visual Studio’s built-in decompilation if you have it, otherwise dnSpyEx.
  • Already using ReSharper or Rider, on Windows: dotPeek is a natural fit, and its symbol server helps the Visual Studio debugger.
  • The file is NativeAOT: none of them; there is no IL. Use native tools.

FAQ

Is dnSpy still maintained? The original repository is archived. dnSpyEx is maintained and released 6.6.0 in June 2026.

Is there a dnSpy for Mac? Not from the dnSpyEx project. ILSpy 11 runs natively on macOS and Linux for decompiling, but it has no debugger.

Are these tools free? ILSpy (MIT) and dnSpyEx (GPL-3.0) are open source. dotPeek is free of charge but closed source. Check each licence before redistributing anything.

Can obfuscated assemblies be read? Decompilers show what the IL contains. Renamed symbols stay renamed, and REA’s import limitations say obfuscated names and restructured control flow are not proof. Only analyze code you own or are authorized to examine, and see the site disclaimer.

Next steps