【问题标题】:How to get "real"/"inner" Func delegate from lambda delegate如何从 lambda 委托中获取“真实”/“内部”Func 委托
【发布时间】:2021-03-23 01:32:54
【问题描述】:

我正在努力实现的目标

我正在尝试实现一个比较两个委托是否相等的方法。
理想情况下,我不想在编译器可以弄清楚时指定可选参数。
所以如果我可以像这里所示那样称呼它会很好:

    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...

我在研究这个时已经学到的东西

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

实际问题

所以我的问题是:是否有机会访问 lambda 的“实际”TargetMethod
“实际”表示匿名类中装箱的那些。因此能够比较TargetMethod

我已经拥有的东西

this 的帮助下,我已经将它与CompareDelegates 一起使用,将lambdas 作为Expression&lt;Func&lt;double&gt;&gt;
缺点:消费者必须指定我想避免的所有可选参数。
供参考(希望它可以帮助遇到此帖子的类似问题的人):

    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);

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

任何建议将不胜感激。

【问题讨论】:

  • 作为旁注:() =&gt; foo.Get(), () =&gt; foo.Get() 是两个不同的匿名方法,它们做同样的事情(调用foo.Get())。它们是不同的,所以它们不能相等。您在第一个非工作示例中尝试做的是比较它们,实际上它们是不同的。在第二个示例中,您查看表达式树的“内部”并比较表达式树的“内容”(即代码() =&gt; foo.Get() 的高级表示)。在第三个示例中,您放弃了匿名方法并直接使用foo.Get 的委托。第二个和第三个例子几乎都可以工作。

标签: c# lambda delegates expression


【解决方案1】:

过了一会儿,我找到了一种感觉像是一种解决方法的方法,但最终对我有用。 不过仍然对任何不同的方法感兴趣!

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 可用as of C#7.3,但seems to be possible for earlier versions as well

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多