Search code examples
.netexpressionclr

Does LambdaExpression.Compile() create an extra assembly which is dynamically loaded?


I want to have several routines which are dynamically created using Expression trees. It seems that the easiest way to use them would be to create LambdaExpression and then call LambdaExpression.
Compile() resulting in a callable delegate.

  1. What are the possible performance penalties of such solution?
  2. What exactly happens during the compilation?
  3. Is an extra assembly with an extra new class type created and dynamically loaded?
  4. Does it happen everytime I call the Compile()?
  5. Is it better to create a new type and include all the routines as static methods of this type by using TypeBuilder and MethodBuilder?

Any suggestions are welcome!


Solution

    1. the emit stage, reflection-initiated delegate creation - and indeed simply building the expression tree - is relatively expensive; if you do that every time it will hurt; if, however, you cache the strategies effectively (so you're not building the expression tree and compiling it constantly), it is perfectly fine and a reasonable way of doing meta-programming
    2. typically, a DynamicMethod is generated - which is a standalone method that avoids all the usual weight of AssemblyBuiler, ModuleBuilder, TypeBuilder etc, and just provides raw access to an ILGenerator to a static-style method; the code then walks the expression tree, emitting the appropriate op-codes via ILGenerator; finally, CreateDelegate is called on the DynamicMethod
    3. no; DynamicMethod tries to avoid this
    4. yes; so try to cache your strategies
    5. depends on context; there are things DynamicMethod can do that TypeBuilder can't (accessibility skipping, for example), and there are things that TypeBuilder can do that DynamicMethod can't (implement interfaces, declare and use fields, etc); if you don't need those things, Expression.Compile() is a pretty good way of avoiding having to learn low-level ref-emit, which is very easy to get wrong (and invalid IL usually kills the runtime)
    6. (you didn't ask a 6, but...) it should also be noted that Expression can choose to spoof the emit, by actually running via an AST interpreter instead, on runtimes that do not allow ref-emit; Compile() returns an entry-point to this interpreter, instead of a delegate to a dynamically compiled method; this option is not available if you use TypeBuilder