我正在玩F#和C#,并希望从C#调用F#代码.
我设法让它在Visual Studio中以相反的方式工作,方法是在同一个解决方案中有两个项目,并在F#项目中添加C#代码的引用.这样做之后,我可以调用C#代码,甚至在调试时逐步执行它.
我想要做的是F#代码来自C#而不是来自F#的C#代码.我在C#项目中添加了对F#项目的引用,但它的工作方式与以前不同.我想知道如果不手动完成这是否可行.
(编辑:我最初在外部链接到这些文件,但我的SVN主机不再允许匿名访问.所以代码现在在这个答案中内联.)
下面是从C#调用F#的工作示例.
正如您所遇到的那样,我无法通过从"添加引用...项目"选项卡中进行选择来添加引用.相反,我必须通过在"添加引用...浏览"选项卡中浏览到F#程序集来手动完成.
------ F#MODULE -----
// First implement a foldl function, with the signature (a->b->a) -> a -> [b] -> a // Now use your foldl function to implement a map function, with the signature (a->b) -> [a] -> [b] // Finally use your map function to convert an array of strings to upper case // // Test cases are in TestFoldMapUCase.cs // // Note: F# provides standard implementations of the fold and map operations, but the // exercise here is to build them up from primitive elements... module FoldMapUCase.Zumbro #light let AlwaysTwo = 2 let rec foldl fn seed vals = match vals with | head :: tail -> foldl fn (fn seed head) tail | _ -> seed let map fn vals = let gn lst x = fn( x ) :: lst List.rev (foldl gn [] vals) let ucase vals = map String.uppercase vals
----- C#UNIT测试模块-----
// Test cases for FoldMapUCase.fs // // For this example, I have written my NUnit test cases in C#. This requires constructing some F# // types in order to invoke the F# functions under test. using System; using Microsoft.FSharp.Core; using Microsoft.FSharp.Collections; using NUnit.Framework; namespace FoldMapUCase { [TestFixture] public class TestFoldMapUCase { public TestFoldMapUCase() { } [Test] public void CheckAlwaysTwo() { // simple example to show how to access F# function from C# int n = Zumbro.AlwaysTwo; Assert.AreEqual(2, n); } class Helper{ public static List mkList(params T[] ar) { List foo = List .Nil; for (int n = ar.Length - 1; n >= 0; n--) foo = List .Cons(ar[n], foo); return foo; } } [Test] public void foldl1() { int seed = 64; List values = Helper .mkList( 4, 2, 4 ); FastFunc > fn = FuncConvert.ToFastFunc( (Converter ) delegate( int a, int b ) { return a/b; } ); int result = Zumbro.foldl ( fn, seed, values); Assert.AreEqual(2, result); } [Test] public void foldl0() { string seed = "hi mom"; List values = Helper .mkList(); FastFunc > fn = FuncConvert.ToFastFunc((Converter )delegate(string a, string b) { throw new Exception("should never be invoked"); }); string result = Zumbro.foldl (fn, seed, values); Assert.AreEqual(seed, result); } [Test] public void map() { FastFunc fn = FuncConvert.ToFastFunc((Converter )delegate(int a) { return a*a; }); List vals = Helper .mkList(1, 2, 3); List res = Zumbro.map (fn, vals); Assert.AreEqual(res.Length, 3); Assert.AreEqual(1, res.Head); Assert.AreEqual(4, res.Tail.Head); Assert.AreEqual(9, res.Tail.Tail.Head); } [Test] public void ucase() { List vals = Helper .mkList("arnold", "BOB", "crAIg"); List exp = Helper .mkList( "ARNOLD", "BOB", "CRAIG" ); List res = Zumbro.ucase(vals); Assert.AreEqual(exp.Length, res.Length); Assert.AreEqual(exp.Head, res.Head); Assert.AreEqual(exp.Tail.Head, res.Tail.Head); Assert.AreEqual(exp.Tail.Tail.Head, res.Tail.Tail.Head); } } }
它应该"正常工作",尽管你可能必须在C#的项目到项目引用工作之前构建F#项目(我忘了).
常见问题的根源是命名空间/模块.如果你的F#代码没有以名称空间声明开头,那么它会被放入一个与文件名同名的模块中,所以例如来自C#你的类型可能显示为"Program.Foo"而不仅仅是"Foo"(如果是Foo)是Program.fs中定义的F#类型.
从这个链接他们似乎有许多可能的解决方案,但似乎最简单的解决方案是:
F#代码:
type FCallback = delegate of int*int -> int;; type FCallback = delegate of int * int -> int let f3 (f:FCallback) a b = f.Invoke(a,b);; val f3 : FCallback -> int -> int -> int
C#代码:
int a = Module1.f3(Module1.f2, 10, 20); // method gets converted to the delegate automatically in C#