Alex Zhukovskiy
Alex Zhukovskiy

Reputation: 10015

Increment value from expression

I want to write a closure and increment it's value but i'm not able to do it. Here is my code

        int i = 0;
        Expression<Func<bool>> closurExpression = () =>
                                                  {
                                                      i++;
                                                      return i != 0;
                                                  };

but i get multuple errors about A lambda expression with a statement body cannot be converted to an expression tree or An expression tree may not contain an assignment operator etc. Is it possible without using external tools as Mono.Cecil etc?


for the question: why am I asking for it. I want to write a simple wrapper (for a signature Func<T,T> at least that will calculate count of calls. For example:

Wrapper<int> wrapper = () => 5;
for(int i = 0; i < 10; i++)
   wrapper();
int calls = wrapper.Calls; // 10;

my first realisation was:

class FunctionWithCounter<T, TResult>
{
    private readonly Func<T, TResult> function;
    public int Calls { get; set; }

    private FunctionWithCounter(Func<T, TResult> function)
    {
        Calls = 0;
        this.function = function;
    }
    public static implicit operator FunctionWithCounter<T, TResult>(Func<T, TResult> func)
    {
        return new FunctionWithCounter<T, TResult>(func);
    }

    public TResult this[T arg]
    {
        get
        {
            Calls++;
            return function(arg);
        }
    }
}

but after i get that recurcive functions won't work properly. For example for this code

int fact(int n) { return n < 2 ? 1 : n * fact(n - 1); }

Calls count will be 1 for any n. So idea is: get source function, and inject increment of Calls for every call of method increment it. All inner calls of method somefunc should be replaced by our method funcWithInjection, in this case we'll catch recursive functions too. Here is my code, but it doesn't work (except inject, so this code really increments a field value, but I cannot add source's method body to the tail and compile it, you can play with it if you will):

public class FunctionWithCounter<T, TResult> where T : new()
{
    private readonly Func<T, TResult> _functionWithInjection;
    private int _calls;
    public int Calls
    {
        get
        {
            return _calls;
        }
    }

    public FunctionWithCounter(Func<T, TResult> function)
    {
        _calls = 0;
        var targetObject = function.Target ?? new object();
        var dynMethod = new DynamicMethod(new Guid().ToString(), typeof(TResult), new[] { targetObject.GetType(), typeof(T), typeof(FunctionWithCounter<T, TResult>) }, true);

        var ilGenerator = GenerateInjection(dynMethod);
        ilGenerator.Emit(OpCodes.Ret);

        var resDelegate = dynMethod.CreateDelegate(typeof(Func<T, FunctionWithCounter<T, TResult>, TResult>), targetObject);
        var functionWithInjection = (Func<T, FunctionWithCounter<T, TResult>, TResult>)resDelegate;
        var targetMethodBody = function.Method.GetMethodBody();
        Debug.Assert(targetMethodBody != null, "mbody != null");

        //here i need to write method body in the tail of dynamic method.

        _functionWithInjection = function;
        _functionWithInjection = t =>
        {
            functionWithInjection(t, this);
            return default(TResult);
        };
        //finally here should be _functionWithInjection = t => functionWithInjection(t, this);
    }

    private ILGenerator GenerateInjection(DynamicMethod method)
    {
        var callsFieldInfo = GetType().GetField("_calls", BindingFlags.NonPublic | BindingFlags.Instance);
        Debug.Assert(callsFieldInfo != null, "callsFieldInfo != null");
        var ilGenerator = method.GetILGenerator();
        ilGenerator.Emit(OpCodes.Nop);
        ilGenerator.Emit(OpCodes.Ldarg_2);
        ilGenerator.Emit(OpCodes.Dup);
        ilGenerator.Emit(OpCodes.Ldfld, callsFieldInfo);
        ilGenerator.Emit(OpCodes.Ldc_I4_1);
        ilGenerator.Emit(OpCodes.Add);
        ilGenerator.Emit(OpCodes.Stfld, callsFieldInfo);
        return ilGenerator;
    }

    public static implicit operator FunctionWithCounter<T, TResult>(Func<T, TResult> func)
    {
        return new FunctionWithCounter<T, TResult>(func);
    }

    public TResult this[T arg]
    {
        get
        {
            return _functionWithInjection(arg);
        }
    }
}

My second realisation was based on Emit API, but it's too complex and It was unfinished for a long time...

So now this is my 3rd try and i want to use expressions. Шt should look something like this:

    public FunctionWithCounter(Expression<Func<T, TResult>> function)
    {
        Action action = () => _calls++;
        Expression<Action> closurExpression = () => action();
        var result = Expression.Block(closurExpression, function);
        _functionWithInjection = Expression.Lambda<Func<T,TResult>>(result).Compile();
    }

sorry for my english, but i really want to realize this idea

Upvotes: 1

Views: 2060

Answers (2)

qbik
qbik

Reputation: 5908

I think that your first approach was cleaner and ok for simple, non-recursive cases. If you allow your wrapped functions to be aware of recursion, you can take it a little further and make the wrapper an argument to the function itself:

 class RecursiveFunctionWithCounter<T, TResult> 
 {
    private readonly Func<T, RecursiveFunctionWithCounter<T, TResult>, TResult> function;
    public int Calls { get; set; }

    public RecursiveFunctionWithCounter(Func<T, RecursiveFunctionWithCounter<T, TResult>, TResult> function)
    {
        Calls = 0;
        this.function = function;
    }


    public TResult this[T arg]
    {
        get
        {
            Calls++;
            return function(arg, this);
        }
    }
 }

And use it like this:

var rw = new RecursiveFunctionWithCounter<int, int>(
        (n, self) => { return n < 2 ? 1 : n * self[n - 1]; }
);

int rr = rw[3]; // rr=6
int rc = rw.Calls; // rc=3

On the other hand, if what you really want to do is instrument some existing methods in your code, consider doing a little Aspect Oriented Programming (with PostSharp for instance, this is an example of an aspect that increments a performance counter on each method call). This way you could just add an attribute like IncrementPerformanceCounterAttribute to your methods and the AOT library will do the rest.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500065

Well, you can do sidestep the rules using Interlocked.Increment:

int i = 0;
Expression<Func<bool>> expression = () => Interlocked.Increment(ref i) != 0;

... but I would be very cautious about doing so. I wouldn't expect many pieces of code which deal with expression trees to handle this terribly cleanly, given the side-effect involved.

The above does seem to work, in terms of behaving how I'd expect it to:

int i = -2;
Expression<Func<bool>> expression = () => Interlocked.Increment(ref i) != 0;
var del = expression.Compile();
Console.WriteLine(del()); // True
Console.WriteLine(del()); // False
Console.WriteLine(del()); // True
Console.WriteLine(del()); // True
Console.WriteLine(i);     // 2

Upvotes: 6

Related Questions