The first time I ever wrote any .Net code was in June 2009, when I started working at Indiana University. My first job was to replace an MS Access application that performed batch processing by representing each data flow task as a form and invoked the data flow task in the form’s Form Load method. Yeah.
Since then, I’ve migrated almost entirely to Web development and along the way there were many new and exciting things to learn. In the process of learning and working on new projects though, I always wondered: how do these experts like Jon Skeet know so much about the code? Why is that some of these MVPs seem to know how the code works, in what order things get called, how they treat exceptional cases and so on?
Answer: They read source code*. And the way you read source code for a DLL is by using a decompiler.
Decompilers, as the word implies, take compiled code and produce source code from it. And one particular decompiler kept being referenced by the experts: Reflector. It seems (seemed?) to be the most popular of them all (even Jon Skeet says in his book that it’s his weapon of choice). Of course, if you are just looking to explore what the IL for a piece of code looks like, you could just use LinqPad (example inspired by C# in Depth!):
But I can’t read that and it’s not very interesting to me. Sadly, turns out Reflector is no longer free, so I looked around for free alternatives and narrowed it down to two: ILSpy and dotPeek. Both generate C# code from DLLs.
ILSpy doesn’t need installing. Just unzip. This is everything:
So how do you use it? Well just load an assembly, double click on a type and watch ILSpy produce C# code. Really, it’s that simple. Here’s what you get when you do that (I picked a simple class from ASP.Net MVC):
Wow, types, names and declarations, embedded strings. How about we expand some of the collapsed sections:
Ah so that’s what MvcForm does when disposed: write out a form closing tag to the output stream. But how it does write out the opening tag? Well, if you recall, in MVC you don’t start an MVCForm by instantiating it directly, you use HtmlHelper.BeginForm. That’s probably in FormExtensions (in keeping with the good practice of putting extension methods in a class named <TypeForWhichExtensionsAreBeingProvided>Extensions). So let’s go there now:
Oh my. A whole bunch of overloads that all call the same method with nulls and default arguments (obviously not using C#’s optional parameters). And at the end, (and this is important), the actual logic for what happens when you call Html.BeginForm. But wait a minute..what’s that EndForm extension method?
Huh, it actually duplicates some of the logic that’s in MvcForm.Dispose, a violation of DRY! In my code, I try very hard to avoid breaking DRY, not just because it makes things easier to maintain, but also because I want to give other developers preferably only one way of doing things (yes I don’t care for the Perl TMTOWTDI nonsense, in case you were wondering). For example, if I had my way, I would force people to always put Forms in using blocks. Regardless, it’s immaterial how I feel. What’s important is: you get to see what the designers of this API were doing and it makes you think about whether there’s a better way to do it.
Suddenly, instead of libraries being these untouchable, forbidden artifacts, they become tangible, imperfect pieces of work by other human beings, just like you and me.
So after I poked around ILSpy a bit, I decided to give dotPeek a try. Extract the folder and…
WOW there is a LOT of stuff in there. Looks like it comes with a lot of ReSharper DLLs (and you’ll see why). Startup dotPeek, browse to the same type (FormExtensions) and you get:
Everything is expanded by default and it looks like Visual Studio. In fact, while poking around, I totally forgot that I was looking at decompilation output and hit ALT+Enter to fix some code! It really makes you forget that you are not looking at the original source. If that’s not enough, check this out:
Yep, that is the absolutely fantastic navigation support you all know and love in Resharper. Yan can also do find usages or paste a stack trace and browse through the types in it. This integration really won me over. Since the day I tried both ILSpy and dotPeek, I’ve pretty much been using dotPeek exclusively and can wholeheartedly recommend it.
So there you have it, a quick and dirty look at what decompilers can do for you. Every .Net developer should be using one. These are the artifacts of our craft and the only way we can get better is by learning from each other. What are you waiting for?
* Ok I don’t know for sure that Jon Skeet reads other people’s source code, but if you read C# in Depth, you’ll find enough evidence that he does use a decompiler to understand what .Net is doing under the covers.