我有F#类库程序集,它包含两个函数:
let add a b = a + b
和
let rec aggregateList list init (op:int -> int -> int) =
match list with
|[] -> init
|head::tail ->
let rest = aggregateList tail init op
op rest head
我有一个C#控制台应用程序,它引用了F#库并尝试执行以下操作:
FSharpList<int> l = new FSharpList<int>(1, new FSharpList<int>(2, FSharpList<int>.Empty));
int result = myFsLibrary.aggregateList(l, 0, myFsLibrary.add);
然而,编译器抱怨[myFsLibrary.add]无法从'方法组'转换为FSharpFunc<int, FSharpFunc<int, int>>
答案 0 :(得分:9)
其他人提供了答案,但我会介入,说你不应该这样做。
不要将F#列表暴露给C#。不要将curried函数暴露给C#。阻抗不匹配在此边界处是可见的,因此最好在跨语言汇编边界处公开常见的框架类型。参见
获取更多建议。
答案 1 :(得分:5)
您可以使用FSharpFunc
委托显式创建函数。在C#中,创建将所有参数作为元组的函数更方便,因此您可以使用FuncConvert
将该函数转换为curry类型。类似的东西:
FuncConvert.FuncFromTupled(new FSharpFunc<Tuple<int, int>, int>(args =>
arags.Item1 + args.Item2))
但是,如果需要从C#代码中调用一些F#函数,建议使用C#友好接口公开函数。在这种情况下,我可以使用Func
委托,第一个参数应该是IEnumerable
而不是F#特定的列表类型:
module List =
let AggregateListFriendly inp init (op:Func<int, int, int>) =
aggregateList (List.ofSeq inp) init (fun a b -> op.Invoke(a, b))
然后你的C#appplication就可以使用:
List.AggregateListFriendly(Enumerable.Range(0, 10), 0, (a, b) => a + b));
答案 2 :(得分:0)
之所以将add导出为普通的.Net样式函数并具有粗略的签名
int add(int, int)
C#和大多数.Net语言将此视为一种方法,它接收2个int
参数并返回单个int
值。 F#虽然没有这样看功能。相反,它会看到add
作为一个函数需要int
并返回一个函数,该函数又接受int
并返回int
。这种函数视图使得实现currying等操作变得非常容易。
为了从世界的C#视图转换为F#,您需要做一些魔术来将方法折叠到自身上。我通过定义一组F#工厂和扩展方法为我做了一些魔术来实现这一目标。例如
[<Extension>]
type public FSharpFuncUtil =
[<Extension>]
static member ToFSharpFunc<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
fun x y -> func.Invoke(x,y)
static member Create<'a,'b,'c> (func:System.Func<'a,'b,'c>) =
FSharpFuncUtil.ToFSharpFunc func
我可以使用此库为add
方法获取适当的F#委托类型,如此
var del = FSharpFuncUtil.Create<int, int, int>(myFsLibrary.add);