CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Patrick Smacchia [MVP C#]


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.






Comments

Lex Y. Li said:

Wonderful post

# March 18, 2008 8:45 AM

Dew Drop - March 18, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - March 18, 2008 | Alvin Ashcraft's Morning Dew

# March 18, 2008 9:57 AM

Peter Ritchie said:

Very interesting, thanks!

# March 18, 2008 10:50 AM

Dimitar E. Dimitrov said:

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

# March 18, 2008 10:53 AM

Jb Evain said:

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

# March 18, 2008 12:15 PM

Bruce Markham said:

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

# March 18, 2008 12:17 PM

Seb Andreo said:

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

# March 19, 2008 4:27 AM

cas said:

Pingback from  cas

# March 19, 2008 6:15 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# March 20, 2008 1:09 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Patrick Smacchia

Patrick Smacchia is a Visual C# MVP involved in software development for over 15 years. After graduating in mathematics and computer science, he has worked on software in a variety of fields including stock exchange, airline ticket reservation system as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the lead developer of the tool NDepend which provides numerous metrics and caveats on any compiled .NET application. He is the author of Practical .NET2 and C#2, a .NET book conceived from real world experience with 647 compilable code listings. Check out Devlicio.us!