Mono.Cecil vs. System.Reflection

 

Mono.Cecil is an open source framework, part of the Mono project, that reads .NET assemblies, but also that
writes .NET assemblies. It is developed by Jean Baptiste Evain 
that now works full-time on the Mono
project.

System.Reflection is a great framework because it is the .NET core base
of several good practices that revolve around the Dynamic / Plug-In / Dependency Injection / Late-Binding kind of
patterns . These patterns currently represent
a successful trend in the .NET community and you can convince yourself by
having a glance at the List of .NET Dependency Injection Containers framework recently
done by Scott Hanselman
.

The object of the current post is to explain that System.Reflection must
not be used to do something else than Late-Binding
like code. Especially, System.Reflection should not be used to do any
kind of code analysis. We (the NDepend team) learnt this lesson the hard way after
more than 3 years of hard-core work with System.Reflection. Hopefully we
refactored our code that does static analysis and it is now (since NDepend
v2.7
) entirely based on Mono.Cecil. We are now plainly satisfied with this
integration (btw, there is no licensing issue in using any code from Mono in a commercial or proprietary application).

So what’s wrong with System.Reflection? It actually comes from its
primary intention of doing Late-Binding
things. As a result it can’t consider code as just raw data. And this leads to
many limitations such as:


  • At any time, browsing the code of an assembly loaded with Reflection might trigger
    a Code Access Security (CAS) exception because the data you’re playing with are
    still considered as code.

  • It has poor performance (I suspect that CAS security checks plays a major role
    in this perf issue).

  • It consumes a lot of memory (here also I suspect that it is because the CLR
    considers data as code) and it is hard to release this memory once you went through
    all the code of an assembly

  • You cannot load 2 different versions of an assembly inside an AppDomain (at
    least not intentionally as I explained here).

  • As a result of the previous limitation you cannot load a Microsoft assembly
    that has a different version than the current CLR version (for example, you
    cannot analyze mscorlib v1.1 from System.Reflection in mscorlib v2.0).



You can
also read this great article from Joel
Pobar
Dodge Common Performance Pitfalls to Craft Speedy Applications if you want more understanding on how
Reflection relies internally on cache that makes memory grow and some benchmark in the average
performance of Reflection in general. Since
.NET v2, System.Reflection supports a kind of read-only mode
but as a heavy user of this feature
I came to the conclusion that most of problems persist with this mode.

 

Another
problem is that System.Reflection suffers from severe limitations
(bugs?!) if you try to analyze out of the beaten path assemblies, I mean, not classic AnyCPU C# or VB.NET assemblies. For
example:
      


  • It tosses many unexpected exceptions when it comes to tricky scenario where
    the assemblies analyzed contain static cctor or complex custom attributes.

  • It doesn’t cop well with complex C++/CLI assemblies (that often don’t respect
    the CLI) and this often leads to unexpected exceptions.

  • It doesn’t cop well with not AnyCPU assemblies (i.e compile option 32/64bits)
    and here also, this often leads to unexpected exceptions.

Finally System.Reflection suffers from some others by-design
limitations because:


  • It doesn’t parse IL.

  • It doesn’t do the distinction about TypeRef and TypeDef as Jm
    Stall
    explains here
    . It means that you will get some UnresolvedException when you try to get
    information about a code element that is referencing some code elements in some
    assemblies not currently loaded in the current AppDomain.

  • It doesn’t know about TypeSpec as
    Jm Stall explains here.
    It means that there will always be some tricky case such as a {ref
    List<U>*} typed parameter where Reflection won’t do distinction between 2
    overloads of a methods.



This post sounds as a bashing of System.Reflection but it is not. This
framework is central to implement late-binding scenarios and personally I had a
lot of fun with it. Also, IMHO Microsoft engineers did an awesome job at
making sure that method calls and object instantiations thought reflection
comes with really high performance (as Ayende 
mentioned recently). See also
this great article from Joe Duffy on CLR method call internals
. And finally, Reflection (and
especially Reflection.Emit) is central to the new dynamic language trend (DLR).
Here what Jim Hugunin, creator of IronPython wrote on its blog: We began to take advantage of the great
new features for dynamic languages already shipping in .NET 2.0 such as
DynamicMethods, blindingly fast delegates and a new generics system that was
seamlessly integrated with the existing reflection infrastructure
.

But when it comes to code analysis, Mono.Cecil is definitely the best
option I came across. The object model is pure and made me learn the CLI much better than any
other sources. The performances are awesome and the support provided by Jb
Evain
is just perfect. These last months’s we reported several bugs and most
of time the fix came within a few hours! As a result, we really have a high
trust in Mono.Cecil and my personal feeling
it shouldn’t be far from bug-free. Also, I didn’t talk at all about the great ability of Cecil to tweak assembly IL (simple because I didn’t need it yet) but you can have a look here to see this amazing feature in action.


This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Marcelo Volmaro

    Reflection also doesn’t work on a custom corelib (so, a library that defines all the system types)… it simply throws an error saying that System.Object does not inherits any type… which is logical, as System.Object is the base type for all the other types.

    Cecil works like a charm…

  • http://devlicio.us/blogs/derik_whittaker/default.aspx Derik Whittaker

    Jason Bock has done a great intro screencast to Cecil over at DimeCasts.

    You can check it out here http://www.dimecasts.net/Casts/CastDetails/59

  • http://www.dotnetguru2.org/sandreo/ Seb Andreo

    With the NDepend 2.7 we can analyze our ~9 million IL instructions code base with out any issues and with a performance benfit about factor 3 !!!!

    Thanks to Cecil and Jb for it hard work and thanks to NDepend Team :o) for this great tool.

    hey guys continue the great job

  • http://www.sharpos.org/ Bruce Markham

    I’m from the SharpOS project (http://www.sharpos.org/) – and we use Cecil to power our 100% C# AOT compiler, which we use to AOT our kernel (and later we will be using it as our JIT).

    Cecil performs very well, and the API is considerably easy to navigate, with no steep learning curve.

    We could have never pulled it off with System.Reflection

  • http://evain.net/blog/ Jb Evain

    Cecil has not been modeled from the FxCop underlying framework (something called CCI). I took inspiration from the great API that Reflector provides though.

  • Dimitar E. Dimitrov

    Mono.Cecil is something completely different than System.Reflection, but can be compared with the FxCop Framework after which it was modelled.

  • http://www.peterritchie.com/blog Peter Ritchie

    Very interesting, thanks!

  • http://lextm.blogspot.com Lex Y. Li

    Wonderful post