C#からF#コードを呼び出す


80

F#とC#で遊んでいますが、C#からF#コードを呼び出したいと思います。

同じソリューションに2つのプロジェクトを配置し、C#コードの参照をF#プロジェクトに追加することで、VisualStudioで逆に機能させることができました。これを行った後、C#コードを呼び出し、デバッグ中にステップスルーすることもできました。

私がやろうとしているのは、F#のC#コードではなく、C#のF#コードです。F#プロジェクトへの参照をC#プロジェクトに追加しましたが、以前のように機能していません。手動で行わなくてもこれが可能かどうか知りたいのですが。


9
特定の問題がない限り、今日のC#プロジェクトからF#プロジェクトへの参照を追加することは「うまくいきます」。.NETアーキテクチャ(言語に依存しない、MSILなど)の基本的な約束または利点の1つであるため、ここで特別なことは何もありません。実際、その逆は奇妙なことです。この恵みにこれ以上何を期待しますか?
Simon Mourier 2018年

回答:


57

以下は、C#からF#を呼び出す実際の例です。

ご覧のとおり、[参照の追加...プロジェクト]タブから選択して参照を追加できませんでした。代わりに、[参照の追加...参照]タブでF#アセンブリを参照して、手動で行う必要がありました。

------ F#モジュール-----

// 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#ユニットテスト-----

// 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
ありがとうございました。「[参照の追加...参照]タブでF#アセンブリを参照して、手動で行う必要がありました。」私のために働いたものです。
ZeroKelvin 2009年

27

C#からのプロジェクト間の参照が機能する前にF#プロジェクトをビルドする必要があるかもしれませんが、「正常に機能する」はずです(忘れています)。

問題の一般的な原因は名前空間/モジュールです。F#コードが名前空間宣言で始まらない場合、ファイル名と同じ名前のモジュールに配置されるため、たとえばC#からは、タイプが「Foo」ではなく「Program.Foo」と表示される場合があります(Fooの場合) Program.fsで定義されているF#タイプです)。


2
モジュール名に関する情報をありがとうございます:)。
ZeroKelvin 2009年

2
ええ、私はそれをブログに書く必要があります、それは多くの混乱を引き起こします。
ブライアン

Fsharpプロジェクト(dll参照のジェネレーター)がCsharp(コンシューマープロジェクト)と同じソリューションにある場合、追加の問題がトリガーされます
George Kargakis 2017年

6

このリンクから彼らはいくつかの可能な解決策を持っているように見えますが、最も単純に見えたのはコメントでした:

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#

val行でエラーが発生します:val f3:FCallback-> int-> int-> int "エラー1定義に予期しないキーワード 'val'があります。このポイントまたは他のトークンまたはその前に不完全な構造化構造が必要です。"
トムスティッケル2011年

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);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.