F#功能与普通CLR方法有很大不同,因为支持currying。例如功能
let inc a = a + 1
将具有Microsoft.FSharp.Core.FSharpFunc<int,int>
类型。它会产生C#互操作性问题。必须专门设计函数才能从C#中轻松调用。
这种设计背后的理由是什么?我相信原因在于支持。但是可以使用闭包来实现currying。例如这段代码:
let add a b = a + b
let inc = add 1
可以很容易地由编译器转换成这个:
let add a b = a + b
let inc = fun x -> add 1 + x
在这种情况下,add和inc都可以是普通System.Func
个对象。我相信这个设计决定背后有一些有趣的原因。
答案 0 :(得分:10)
据我记忆,在F#中为函数设置单独类型的动机是性能(在早期版本FSharpFunc<...>
中实际上称为FastFunc<...>
)。我不完全确定最近的发展(我确信F#团队做了一些测试,以确定代理是否可以在Visual Studio 2010中工作),但这是我如何理解这个问题:
如果您有一个函数add : int -> int -> int
,那么该函数可以表示为委托Func<int, Func<int, int>>
(使用咖喱表示)。问题是,您经常需要使用add 1 2
等参数调用它。
使用带有嵌套Func
类型的表示,这将编译为add.Invoke(1).Invoke(2)
。
但是,在编译add
之类的函数时,F#编译器实际上创建了一个新类,比如AddClass
,其中从<{1}} 继承了, 使用两个参数添加额外的FSharpFunc<int, FSharpFunc<int, int>>
重载。这意味着,在大多数情况下,Invoke
可以编译为只有一个调用add 1 2
。
这种设计使F#代码更快。它使互操作性稍微复杂化,但不是太多。编写一个带代理的F#成员或函数相当容易:
add.Invoke(1, 2)
(您只需要添加类型注释然后调用let foo (inc : Func<int, int>) = inc.Invoke(41)
- 但您也可以使用f.Invoke
作为第一类值并将其传递给其他函数)
答案 1 :(得分:1)
我的猜测是F#表示支持大步语义,而System.Func
则不支持。
答案 2 :(得分:1)
与C#互操作的最佳方式是将所有内容包装在类/成员中。 C#永远不必看到成员的内部运作。