[英]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接受一个TMessage,它必须从InputMessageParent继承并返回一个简单的Result类型。 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:'a
和e:'b
),但是在从上下文中已知预期类型为Func<_,_>
编译器将强制并将lambda表达式编译为Func
。
这个例外是为了简化与大量使用lambda表达式的.NET库的互操作,例如LINQ。 但是,此异常不适用于通过名称引用命名函数。 它可能应该适用,但事实并非如此。
如果你不想拥有一个毫无意义的lambda表达式,唯一的另一种方法是通过调用它的构造函数并将函数传递给它来显式创建一个Func<_,_>
类型的对象,然后将该对象传递给DoWork
:
builder.DoWork("param1", Func<_,_> actionImpl)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.