Jun 16, 2016

Querying .NET assemblies with LINQPad

Since 2008 I have not only been playing / working with .NET on a daily basis; I've also done quite a lot of work with .NET assemblies and, as result, quite frequently I find myself trying to answer questions like:
  • How many types in the assembly are value types?
  • How many types exposes more than x properties?
  • Do we have any interfaces / types that does not conform to some specific naming convention?
  • etc.
In order to answer these questions I used to either write a quick program (with the help of Mono.Cecil) or ended up having some fun time with regular expressions (of course, running these regular expressions on the source code instead of the built assemblies). These solutions work relatively well but they have some serious drawbacks: i) lots of repetitive work, ii) it is harder to extract information from the source than from assemblies; iii) some times I don't have any source code at all, only the assembly, etc.

Since last Hackweek this was set to change! For some reason I can't explain (it is as if I have been in Mars for the last couple of years ;) I failed to even realize how powerful / flexible LINQPad has become; it is not the case that I was not aware about the tool itself; I knew LINQPad for some time (lets say, for 6 ~ 7 years) but I've never used it util last May; at that time a bunch of team mates were using it to quickly check generated IL for some C# code snippet and since I was participating in the same project I also needed to the same, so I decided to give it a try.

After returning to my daily tasks I sat one day and started experimenting with the idea of running queries on .NET assemblies for a couple of hours, and ended up really happy with the outcome. Actually, it was so easy that I have no idea why it took me so long to start using it: basically we can use .NET reflection and LINQ for Objects (as you can see in the following example):

var a = Assembly.LoadFrom(@"S:\Unity\Editor\Data\Managed\UnityEngine.dll");
var result = from t in a.GetTypes()
             where Char.IsLower(t.Name[0])
             select t.FullName;

result.Dump();

or, in other words, show me all the types with a name starting with a lowercase char!

That is it, simple like that. The best part is that it is plain C# using LINQ and reflection to navigate through the .NET metadata.

Of course there's (almost) nothing specific to LINQPad in that code snippet; to me, the value that LINQPad aggregates lies on how easy and natural it looks like to use it to type such queries without the need to write / compile a separate application and the quick iteration times it enables.

If you want to try it also just copy / past that code to LINQPad, change the assembly path to one that makes sense to you and run (by pressing F5):




Once we have a way to easily run standard LINQ queries over assemblies we can unleash our imagination and rely on LINQPad power. But, before you get too excited, this approach does have one caveat though: you'll need to have a good grasp about .NET reflection to be able to use it effectively.

In my next post I'll write a custom LINQPad Driver in order to use Mono.Cecil instead of .NET Reflection making it even easier / more flexible to use!

Have fun!

Leia este post em Português.

No comments: