如何将F#函数从C#app传递到另一个F#函数?

时间:2011-05-17 18:15:12

标签: f# class-library c#-to-f#

我有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>>

3 个答案:

答案 0 :(得分:9)

其他人提供了答案,但我会介入,说你不应该这样做。

不要将F#列表暴露给C#。不要将curried函数暴露给C#。阻抗不匹配在此边界处是可见的,因此最好在跨语言汇编边界处公开常见的框架类型。参见

http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/fsharp-component-design-guidelines.pdf

获取更多建议。

答案 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);
相关问题