如何从 lambda 委托获取 "real" / "inner" Func 委托



我正在努力实现的目标

我正在尝试实现一种比较两个代理是否相等的方法
理想情况下,我不想在编译器可以计算出可选参数的时候指定它们
所以,如果我能像这里所示的那样称呼它,那就太好了:

public class Foo { public double Get(double d = 0.0) { return d; } }
[TestMethod]
public void TestMethod()
{
var foo = new Foo();
bool equal = CompareDelegates(() => foo.Get(), () => foo.Get());
// equal will be false here
}
private bool CompareDelegates(Func<double> d1, Func<double> d2)
{
return d1 == d2;
}

使用此实现,CompareDelegates将始终返回false。。。

我在研究这件事时已经学到了什么

  • delgate可以通过其TargetMethod属性的组合来唯一识别,因此直观地假设上面的例子返回true
  • 之所以没有,是因为编译器在评估lambda时创建了一个匿名类。这个类包含一个引用foo的字段和每个lambda的一个方法。这导致两个委托具有不同的Method属性,从而导致相等性测试失败

实际问题

所以我的问题是:是否有机会访问";实际的";λ的CCD_ 6和CCD_
带有"实际的";这意味着那些被装箱在匿名类中的。从而能够比较CCD_ 8和CCD_。

我已经拥有的

在这方面的帮助下,我已经使它与CompareDelegates一起工作,将lambdas作为Expression<Func<double>>
缺点:消费者必须指定所有可选参数,我希望避免这些参数
供参考(希望它能帮助遇到类似问题的人(:

private bool CompareDelegates(Expression<Func<double>> d1, Expression<Func<double>> d2)
{
var actual1 = GetActualTargetAndMethod(d1);
var actual2 = GetActualTargetAndMethod(d2);

return actual1.method == actual2.method && actual1.target == actual2.target;
// return actual1 == actual2; // as of C#7.3
}

private (object target, MethodInfo method) GetActualTargetAndMethod(Expression<Func<double>> expression)
{
// The expression is a lambda expression with a method call body.
var lambda = (LambdaExpression)expression;
var methodCall = (MethodCallExpression)lambda.Body;
// The method is called on a member of some instance.
var member = (MemberExpression)methodCall.Object;
// The member expression contains an instance of the anonymous class that
// defines the member...
var constant = (ConstantExpression)member.Expression;
var anonymousClassInstance = constant.Value;
// ...and the member itself.
var calledClassField = (FieldInfo)member.Member;
// With an instance of the anonymous class and the field, we can get its value.
return (calledClassField.GetValue(anonymousClassInstance), methodCall.Method);
}

当然你可以做:

private bool CompareDelegates(Func<double, double> d1, Func<double, double> d2)
{
return d1 == d2;
}
// being called like: equal = CompareDelegates(foo.Get, foo.Get);

它看起来不错,也可以使用可选参数
缺点:您必须为可能存在的所有可选参数编写重载。

如有任何建议,我们将不胜感激。

过了一段时间,我找到了一种解决方法,但最终对我有效。仍然对任何不同的方法感兴趣!

private bool CompareDelegates<T>(T d1, T d2) where T : Delegate
{
return d1 == d2;
}

用法:

bool equal = CompareDelegates<Func<double,double>>(foo.Get, foo.Get); // returns true

使用此解决方案,您不必指定可选参数,它适用于Foo.Get(...)可能具有的任何数量的参数。遗憾的是,编译器本身无法推断类型参数T

注意:where T : Delegate从C#7.3开始就可用,但早期版本似乎也可以使用。

最新更新