F# 代码调用包含异常运行的 Func 参数的 c# 方法

F# code invoking a c# method containing a Func parameter behaving strangely

我们最近遇到了 F# 代码调用 C# 代码的问题。我已尽可能简单地简化了问题。 C#代码如下:

using System;

namespace CSharpLib
{
    public abstract class InputMessageParent
    {
        public readonly Guid Id;

        protected InputMessageParent(Guid id) { this.Id = id; }
    }

    public class Result { public string ResultString; }

    public enum FunctionResult
    {
        None,
        Good,
        Bad
    }

    public class ConfigurationBuilder
    {
        public Result DoWork<TMessage>(
            string param1,
            Func<TMessage, FunctionResult> action)
            where TMessage : InputMessageParent
        {
            return new Result() { ResultString = "Good" };
        }
    }
}

F#代码需要调用ConfigurationBuilders DoWork函数。请注意,DoWork 函数采用两个参数,一个简单的字符串和一个 Func 作为第二个参数。 Func 接受一个必须从 InputMessageParent 继承的 TMessage,它 returns 是一个简单的结果类型。 F#代码如下:

open System

type InputMessageConcreteTypeA =
    inherit CSharpLib.InputMessageParent
    val Property1:string
    new (id, property1Value) =
        {
            inherit CSharpLib.InputMessageParent(id)
            Property1 = property1Value
        }

[<EntryPoint>]
let main argv =

    let actionImpl (input:InputMessageConcreteTypeA) =
    CSharpLib.FunctionResult.Good

    let builder = new CSharpLib.ConfigurationBuilder()
    builder.DoWork("param1", actionImpl) |> ignore

    0

此代码无法编译。 actionImpl 类型是 InputMessageConcreteTypeA -> CSharpLib.FunctionResult,这正是 DoWork 期望的第二个参数的类型,但它却给我以下错误:This expression was expected to have type 'Func<'a,CSharpLib.FunctionResult>' but here has type 'b -> CSharpLib.FunctionResult'

有趣的是,如果我将代码更改为以下代码,它会编译:

[<EntryPoint>]
let main argv =

let actionImpl (input:InputMessageConcreteTypeA) =
    CSharpLib.FunctionResult.Good

let builder = new CSharpLib.ConfigurationBuilder()
builder.DoWork("param1", fun input -> actionImpl(input)) |> ignore

0

为什么代码会针对与 actionImpl 完全相同的类型定义的内联匿名函数进行编译,但如果我直接传递 actionImpl 则不会编译?内联匿名函数看起来毫无意义,但却是我们目前唯一的解决方案。有没有更好的方法?

F#函数类型'a -> 'b不是,其实和C#类型Func<a,b>一样

它们不同的原因尚无定论,但结果是您不能随心所欲地将一种类型当作另一种类型。类型不匹配。这就是编译器告诉您的内容:预期具有类型 Func<...>,但此处具有类型 'b -> ...

但是,对于 lambda 表达式,编译器会抛出异常。通常,lambda 表达式 fun x -> e 的类型为 'a -> 'b(其中 x:'ae:'b),但在上下文中已知预期类型为 Func<_,_> 编译器将强制并将 lambda 表达式编译为 Func.

创建此异常是为了简化与大量使用 lambda 表达式的 .NET 库(例如 LINQ)的互操作。但此例外不适用于按名称引用命名函数。它可能应该适用,但它不适用。

如果您不想使用毫无意义的 lambda 表达式,唯一的其他方法是通过调用其构造函数并将函数传递给它来显式创建 Func<_,_> 类型的对象,然后传递该反对 DoWork:

builder.DoWork("param1", Func<_,_> actionImpl)