【问题标题】:F# passing in a C# method into another C# method expecting Func<> parameterF# 将 C# 方法传入另一个需要 Func<> 参数的 C# 方法
【发布时间】:2020-02-24 12:50:04
【问题描述】:

在 c# dll 中,我有一个方法,它采用 func 参数:

public static AttrDiffRule Create<T>(string a_attr, string b_attr, Func<IAttrProxy,IAttrProxy,T,bool> parametricRule, T ruleParam, string desc = null)

以及一些预定义的默认方法:

public static bool NumberEqualsWithTolerance(IAttrProxy a, IAttrProxy b, double tolerance)

现在在 C# 中使用它时,我可以编写以下代码并且它可以工作:

var tmp = DefaultRules.Create("fds", "fds", DefaultRules.NumberEqualsWithTolerance, 10.0);

但是,在 F# 中:

let attrRule = DefaultRules.Create("fd","fdsa", DefaultRules.NumberEqualsWithTolerance, 89.)

给出语法错误:“错误 FS0002 此函数接受太多参数,或在不期望函数的上下文中使用”

将 C# 静态方法传递给期望 F# 中的 Func 的参数的正确方法是什么?

重要的是实际传入函数,而不是 lambda 包装器,因为 Create 方法的工作是使用参数函数的 MethodInfo,它被 lambda 包装器隐藏。

传入的函数没有重载,也尝试过在适当的位置指定类型

(DefaultRules.NumberEqualsWithTolerance : Func<IAttrProxy,IAttrProxy,float,bool>)

【问题讨论】:

    标签: f#


    【解决方案1】:

    这是一个 F# 代表您非常周到的案例 - 通过帮助您编写更惯用的 F#。

    在 .NET 中,您实际上并没有传递函数,就好像它是成员引用一样,而是传递了 Func&lt;&gt; 类型的委托对象。当委托对象具有必要的类型信息时,委托对象的构造由 C# 隐式完成。

    如果我们将其重构为实际的委托类型,我们可以更清楚地看到这一点:

    public delegate bool ParametricRule<T>(IAttrProxy a, IAttrProxy b, T value);
    
    public static AttrDiffRule Create<T>(string a_attr, string b_attr, ParametricRule<T> parametricRule, T ruleParam, string desc = null)
    {
        return default;
    }
    

    如果你尝试在 F# 中构造一个ParametricRule,你会看到它的类型是:

    ParametricRule(IAttrProxy -> IAttrProxy -> 'a -> bool)
    

    理由是这样您可以使用常规 F# 函数,而不是一些非 F#ish 元组输入函数。这就是为什么它在您的情况下不起作用的原因。 因为您正试图将 C# 中的元组版本扔回去。

    因此,如果您将 C# 实现重构为:

    protected static bool NumberEqualsWithToleranceImpl(IAttrProxy a, IAttrProxy b, float tolerance)
    {
        return default;
    }
    
    public static ParametricRule<float> NumberEqualsWithTolerance => NumberEqualsWithToleranceImpl;
    

    您会看到它在 F# 和 C# 中都像您期望的那样工作。

    let attrRule = DefaultRules.Create("fd","fdsa", DefaultRules.NumberEqualsWithTolerance, 89.0f) //compiles, yay!
    

    【讨论】:

      【解决方案2】:

      有时,在将方法作为函数参数传递时,类型解析会遇到问题,因为方法上可能存在重载,导致签名不明确。您可以将函数包装在传递参数的 lambda 中。

      let attrRule = 
          DefaultRules.Create(
              "fd",
              "fdsa", 
              (fun a b tolerance -> DefaultRules.NumberEqualsWithTolerance(a, b, tolerance)), 
              89.0)
      
      

      【讨论】:

      • 不幸的是,这并没有解决问题,因为约束不是原始问题的一部分:.Create 方法的主要工作是使用传入函数的 MethodInfo,它被 lambda 包装覆盖。
      • @Balinth 如果意图使用MethodInfo,那么您应该只传递MethodInfo。你的函数不应该依赖调用者知道实现细节;任何符合签名的功能都应该起作用。因此,如果你真正需要的是一个MethodInfo 对象,那么你的形参中应该有一个MethodInfo 对象。
      • 我的意思是,该方法的重点是您不必为最常用的情况传递额外的描述,已经有一个直接采用字符串的重载,您可以在其中当您传入更具体的 lambda 规则时,指定方法将从 MethodInfo 中获取什么。显然,一个额外的参数不会影响工具的成败,但这并不是要了解实现,而是要能够传入应该能够传入的参数。
      猜你喜欢
      • 2016-12-21
      • 2011-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多