1

This is all about the Compile method of Expression Type. Sorry being naïve since I am a late comer. I have been reading about building expression in order to enables dynamic modification of executable code. And It make sense for me when it comes to emitting lambda expression from a given expression tree as long as for the varying inputs/environment (say for different values for any given constant/parameter/member expression). I presume, it would be ideal if I could cache (re-use) lambda those are generated/compiled from an expression tree provided there is no change in the environment.

Question: Does CLR always emit lambda expression even if I have there is no change in the environment? If so, what is the best was to avoid compilation of expression from lambda if there is no change in the environment?

svick
  • 236,525
  • 50
  • 385
  • 514
S.N
  • 4,910
  • 5
  • 31
  • 51
  • 1
    I think that depends on how exactly are you using expression trees. Could you add a specific example of how do you imagine using them? – svick Jun 17 '13 at 15:24

3 Answers3

3

CLR doesn't cache lambda expressions, Compile() each time returns a new delegate.

But it should be easy to cache, via something like this:

public Func<T> Get<T>(Expression<Func<T>> expression)
{
    string key = expression.Body.ToString();

    Func<T> result;
    if (!_cache.TryGetValue(key, out result)) {
        result = expression.Compile();
        _cache.Add(key, result);
    }

    return result;
}
andreister
  • 13,693
  • 2
  • 44
  • 45
  • 1
    I think using `ToString()` to compare equality is fragile. For example, you can create two `Expression`s, both of which will have string representation of `(x, x) => (x + x)`, but will return different result. Although the alternative (properly structurally comparing the `Expression`s) is not trivial. – svick Jun 17 '13 at 16:49
  • Also, your code wouldn't actually work, because you can't have generic fields (`_cache` would have to be `Dictionary>`, which is not possible). But that can be easily fixed by a cast. – svick Jun 17 '13 at 16:51
2

Lambda expressions is just a way to represent a piece of code: call this, call that, compare these arguments, return something, etc. Almost the same way you do it from code editor, or JIT does from IL.

Compile emits a delegate from particular lambda expression. Once you have compiled lambda into delegate, the delegate remains unchanged (the lambda remains unchanged too, because it is immutable).

This doesn't mean, that delegate can't accept any arguments or call any method of any object. This just means, that delegate's IL doesn't change. And yes, you can cache compiled delegate instance.

Dennis
  • 37,026
  • 10
  • 82
  • 150
0

Calling Compile() will return a new delegate each time, and each time new MSIL code is emitted. This is slow and effectively creates a memory leak, since MSIL code is not subject to garbage collection. I created a library that offers cached compilation, which in fact compares the structure of the expressions correctly and enables reusing the cached delegates. All constants and closures are automatically replaced by parameters and get re-inserted into the delegate in an outer closure. That avoids the leaking memory and is much faster. Check it out here: https://github.com/Miaplaza/expression-utils

  • Your comment got me questioning if I should use Compile(), but I also found a number of posts saying that there should be no memory leak - are there some conditions where it will and other where it wont? Could you explain please. Thanks in advance! Example of a post: https://stackoverflow.com/questions/3940261/c-sharp-compiled-lambda-expressions-instance-creation-and-or-garbage-collection – AlexVPerl Nov 15 '19 at 19:31