表达式的增量值
本文关键字:表达式 | 更新日期: 2023-09-27 18:12:56
我想写一个闭包并增加它的值,但我不能这样做。这是我的代码
int i = 0;
Expression<Func<bool>> closurExpression = () =>
{
i++;
return i != 0;
};
,但我得到关于A lambda expression with a statement body cannot be converted to an expression tree
或An expression tree may not contain an assignment operator
等多个错误。有没有可能不使用外部工具作为Mono。塞西尔等等?
的问题:为什么我要求它。我想写一个简单的包装器(为签名Func<T,T> at least
,将计算调用的计数。例如:
Wrapper<int> wrapper = () => 5;
for(int i = 0; i < 10; i++)
wrapper();
int calls = wrapper.Calls; // 10;
我的第一个意识是:
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);
}
}
}
但是当我得到这个后,递归函数就不能正常工作了。例如以下代码
int fact(int n) { return n < 2 ? 1 : n * fact(n - 1); }
对于任意n,调用计数将为1。因此思路是:获取源函数,并为每次方法调用注入调用的增量。方法somefunc
的所有内部调用都应该替换为方法funcWithInjection
,在这种情况下,我们也将捕获递归函数。这是我的代码,但它不起作用(除了注入,所以这段代码实际上增加了一个字段值,但我不能将源的方法体添加到尾部并编译它,如果你愿意,你可以玩它):
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);
}
}
}
我的第二个实现是基于Emit API,但它太复杂了,而且很长一段时间都没有完成…
所以现在这是我的第三次尝试,我想使用表达式。Шt应该看起来像这样:
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();
}
很抱歉我的英语不好,但我真的很想实现这个想法
你可以使用Interlocked.Increment
:
int i = 0;
Expression<Func<bool>> expression = () => Interlocked.Increment(ref i) != 0;
…但我会非常谨慎这样做。考虑到所涉及的副作用,我不希望许多处理表达式树的代码能够非常干净地处理这个问题。
上面的似乎可以工作,就我所期望的行为而言:
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
我认为您的第一种方法更清晰,对于简单的非递归情况也可以。如果允许包装的函数知道递归,可以更进一步,将包装器作为函数本身的参数:
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);
}
}
}
并像这样使用:
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
另一方面,如果你真正想做的是在你的代码中插入一些现有的方法,考虑做一点面向方面的编程(例如,用PostSharp,这是一个在每个方法调用上增加性能计数器的方面的例子)。这样,您只需在方法中添加像IncrementPerformanceCounterAttribute
这样的属性,AOT库就会完成其余的工作。