最近,我们遇到了一个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" };
}
}
}
Run Code Online (Sandbox Code Playgroud)
F#代码需要调用ConfigurationBuilders DoWork函数。请注意,DoWork函数采用两个参数,一个简单的字符串和一个Func作为第二个参数。Func接受必须从InputMessageParent继承的TMessage,并返回简单的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
Run Code Online (Sandbox Code Playgroud)
此代码无法编译。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
Run Code Online (Sandbox Code Playgroud)
为什么代码会为内联匿名函数编译,该匿名匿名函数与actionImpl具有完全相同的类型定义,但是如果我直接传递actionImpl就不会编译?内联匿名函数看起来毫无意义,但这是我们目前唯一的解决方案。有没有更好的办法?
的F#函数类型'a -> 'b是不,事实上,同为C#类型Func<a,b>。
它们之所以不同的原因有点儿争议,但结果是您不能随心所欲地将一种类型传递给另一种。类型不匹配。这就是编译器告诉您的内容:预期类型为Func <...>,但是这里的类型为'b-> ...
但是,对于lambda表达式,编译器会例外。通常,lambda表达式fun x -> e将具有类型'a -> 'b(where x:'a和e:'b),但是如果从上下文中已经知道预期类型是Func<_,_>编译器,则编译器会将lambda表达式编译为a Func。
设置此异常是为了简化与大量使用lambda表达式的.NET库(例如LINQ)的互操作。但是,此异常不适用于通过名称来引用命名函数。它可能应该适用,但不是。
如果您不希望有无意义的lambda表达式,唯一的另一种方法是Func<_,_>通过调用类型的构造函数并将函数传递给它来显式创建类型的对象,然后将该对象传递给DoWork:
builder.DoWork("param1", Func<_,_> actionImpl)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
105 次 |
| 最近记录: |