Gọi mã F # từ C #


80

Tôi đang chơi với F # và C #, và muốn gọi mã F # từ C #.

Tôi đã quản lý để làm cho nó hoạt động theo cách khác trong Visual Studio bằng cách có hai dự án trong cùng một giải pháp và thêm một tham chiếu của mã C # vào dự án F #. Sau khi thực hiện việc này, tôi có thể gọi mã C # và thậm chí xem qua nó trong khi gỡ lỗi.

Những gì tôi đang cố gắng làm là mã F # TỪ C # thay vì mã C # từ F #. Tôi đã thêm một tham chiếu đến dự án F # vào dự án C #, nhưng nó không hoạt động như trước đây. Tôi muốn biết nếu điều này có thể thực hiện được mà không cần làm theo cách thủ công.


9
Trừ khi bạn có vấn đề cụ thể, việc thêm tham chiếu đến dự án F # từ C # one ngày hôm nay "chỉ hoạt động". Không có gì đặc biệt ở đây, vì đó là một trong những lời hứa hoặc lợi ích cơ bản của kiến ​​trúc .NET (Ngôn ngữ bất khả tri, MSIL, v.v.). Trong thực tế, điều ngược lại sẽ là kỳ lạ. Bạn còn mong đợi gì hơn nữa cho tiền thưởng này?
Simon Mourier

Câu trả lời:


57

Dưới đây là một ví dụ hoạt động của việc gọi F # từ C #.

Như bạn đã gặp, tôi không thể thêm tham chiếu bằng cách chọn từ tab "Thêm tham chiếu ... Dự án". Thay vào đó, tôi phải làm điều đó theo cách thủ công, bằng cách duyệt đến hội F # trong tab "Thêm tài liệu tham khảo ... Duyệt".

------ 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ÁC BÀI KIỂM TRA ĐƠN VỊ C # CHO MODULE -----

// 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<T>
        {
            public static List<T> mkList(params T[] ar)
            {
                List<T> foo = List<T>.Nil;
                for (int n = ar.Length - 1; n >= 0; n--)
                    foo = List<T>.Cons(ar[n], foo);
                return foo;
            }
        }


        [Test]
        public void foldl1()
        {
            int seed = 64;
            List<int> values = Helper<int>.mkList( 4, 2, 4 );
            FastFunc<int, FastFunc<int,int>> fn =
                FuncConvert.ToFastFunc( (Converter<int,int,int>) delegate( int a, int b ) { return a/b; } );

            int result = Zumbro.foldl<int, int>( fn, seed, values);
            Assert.AreEqual(2, result);
        }

        [Test]
        public void foldl0()
        {
            string seed = "hi mom";
            List<string> values = Helper<string>.mkList();
            FastFunc<string, FastFunc<string, string>> fn =
                FuncConvert.ToFastFunc((Converter<string, string, string>)delegate(string a, string b) { throw new Exception("should never be invoked"); });

            string result = Zumbro.foldl<string, string>(fn, seed, values);
            Assert.AreEqual(seed, result);
        }

        [Test]
        public void map()
        {
            FastFunc<int, int> fn =
                FuncConvert.ToFastFunc((Converter<int, int>)delegate(int a) { return a*a; });

            List<int> vals = Helper<int>.mkList(1, 2, 3);
            List<int> res = Zumbro.map<int, int>(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<string> vals = Helper<string>.mkList("arnold", "BOB", "crAIg");
            List<string> exp = Helper<string>.mkList( "ARNOLD", "BOB", "CRAIG" );
            List<string> 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);
        }

    }
}

1
Cảm ơn bạn. "Tôi đã phải làm điều đó theo cách thủ công, bằng cách duyệt đến cụm F # trong tab 'Thêm tài liệu tham khảo ... Duyệt qua'." là những gì đã làm việc cho tôi.
ZeroKelvin

27

Nó sẽ 'chỉ hoạt động', mặc dù bạn có thể phải xây dựng dự án F # trước khi tham chiếu từ dự án này sang dự án khác từ C # hoạt động (tôi quên).

Một nguồn phổ biến của các vấn đề là không gian tên / mô-đun. Nếu mã F # của bạn không bắt đầu bằng khai báo không gian tên, nó sẽ được đưa vào một mô-đun có cùng tên với tên tệp, vì vậy, ví dụ từ C #, kiểu của bạn có thể xuất hiện là "Program.Foo" thay vì chỉ "Foo" (nếu Foo là kiểu F # được định nghĩa trong Program.fs).


2
Cảm ơn bạn về thông tin liên quan đến tên mô-đun :).
ZeroKelvin

2
Vâng, tôi cần phải viết blog đó, nó gây ra rất nhiều nhầm lẫn.
Brian

Một vấn đề khác được kích hoạt, khi dự án Fsharp (máy phát điện của tham chiếu dll) là trong cùng một giải pháp với CSharp (dự án tiêu dùng)
George Kargakis

6

Từ liên kết này, họ dường như có một số giải pháp khả thi, nhưng giải pháp có vẻ đơn giản nhất là một nhận xét:

Mã 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

Mã C #:

int a = Module1.f3(Module1.f2, 10, 20); // method gets converted to the delegate automatically in C#

Tôi gặp lỗi trên dòng val: val f3: FCallback -> int -> int -> int "Lỗi 1 Từ khóa không mong muốn 'val' trong định nghĩa. Dự kiến ​​cấu trúc có cấu trúc không hoàn chỉnh tại hoặc trước điểm này hoặc mã thông báo khác."
Tom Stickel

4

// Test.fs:

module meGlobal

type meList() = 
    member this.quicksort = function
        | [] -> []  //  if list is empty return list
        | first::rest -> 
            let smaller,larger = List.partition((>=) first) rest
        List.concat[this.quicksort smaller; [first]; this.quicksort larger]

// Test.cs:

List<int> A = new List<int> { 13, 23, 7, 2 };
meGlobal.meList S = new meGlobal.meList();

var cquicksort = Microsoft.FSharp.Core.FSharpFunc<FSharpList<IComparable>,     FSharpList<IComparable>>.ToConverter(S.quicksort);

FSharpList<IComparable> FI = ListModule.OfSeq(A.Cast<IComparable>());
var R = cquicksort(FI);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.