简体   繁体   English

调用包含Func参数的c#方法的F#代码表现奇怪

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

We came across an issue recently with F# code calling into C# code. 我们最近遇到了一个问题,F#代码调用了C#代码。 I have simplified the issue down as simple as I can. 我尽可能地简化了问题。 The C# code is as follows: 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" };
        }
    }
}

The F# code needs to call the ConfigurationBuilders DoWork function. F#代码需要调用ConfigurationBuilders DoWork函数。 Note that the DoWork function takes two parameters, a simple string and a Func as a second parameter. 请注意,DoWork函数有两个参数,一个简单的字符串和一个Func作为第二个参数。 The Func takes in a TMessage which must inherit from InputMessageParent and it returns a simple Result type. Func接受一个TMessage,它必须从InputMessageParent继承并返回一个简单的Result类型。 The F# code is as follows: 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

This code does not compile. 此代码无法编译。 The actionImpl type is InputMessageConcreteTypeA -> CSharpLib.FunctionResult, this is exactly the type of the second parameter that DoWork is expecting but instead it gives me the following error: This expression was expected to have type 'Func<'a,CSharpLib.FunctionResult>' but here has type 'b -> CSharpLib.FunctionResult' actionImpl类型是InputMessageConcreteTypeA - > CSharpLib.FunctionResult,这正是DoWork期望的第二个参数的类型,但它给了我以下错误: This expression was expected to have type 'Func<'a,CSharpLib.FunctionResult>' but here has type 'b -> CSharpLib.FunctionResult'

Interestingly, if I change the code to the following its does compile: 有趣的是,如果我将代码更改为以下代码,则会编译:

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

Why does the code compile for the inline anonymous function which is the exact same type definition as actionImpl but wont compile if I just pass actionImpl directly? 为什么代码编译为内联匿名函数,它与actionImpl完全相同,但如果我直接传递actionImpl则不会编译? The inline anonymous function looks pretty pointless but is the only solution that we have at the moment. 内联匿名函数看起来毫无意义,但它是我们目前唯一的解决方案。 Is there a better way? 有没有更好的办法?

The F# function type 'a -> 'b is not , in fact, the same as the C# type Func<a,b> . 的F#函数式'a -> 'b 不是在事实上,同样作为C#类型Func<a,b>

The reason for why they're different is a bit moot, but the upshot is that you can't just willy-nilly pass one type off as the other. 他们之所以有所不同的原因有点没有实际意义,但结果是你不能随便将一种类型传递给另一种类型。 Type mismatch. 类型不匹配。 This is what the compiler is telling you: expected to have type Func<...>, but here has type 'b -> ... 这是编译器告诉你的: 期望有类型Func <...>,但这里有类型'b - > ...

For lambda expressions, however, the compiler makes an exception. 但是,对于lambda表达式,编译器会发生异常。 Normally, a lambda expression fun x -> e will have type 'a -> 'b (where x:'a and e:'b ), but in cases where it's already known from the context that the expected type is Func<_,_> the compiler will oblige and compile the lambda expression as a Func . 通常,lambda表达式fun x -> e将具有类型'a -> 'b (其中x:'ae:'b ),但是在从上下文中已知预期类型为Func<_,_>编译器将强制并将lambda表达式编译为Func

This exception was made to ease interop with .NET libraries that make heavy use of lambda expressions, such as LINQ. 这个例外是为了简化与大量使用lambda表达式的.NET库的互操作,例如LINQ。 But this exception doesn't apply to referencing named functions by their name. 但是,此异常不适用于通过名称引用命名函数。 It probably should apply, but it doesn't. 它可能应该适用,但事实并非如此。

If you don't want to have a pointless lambda expression, the only other way to go is to explicitly create an object of type Func<_,_> by calling its constructor and passing the function to it, then pass that object to DoWork : 如果你不想拥有一个毫无意义的lambda表达式,唯一的另一种方法是通过调用它的构造函数并将函数传递给它来显式创建一个Func<_,_>类型的对象,然后将该对象传递给DoWork

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM