2009-01-25 8 views
54

Je joue avec F # et C#, et je voudrais appeler le code F # de C#.Appeler le code F # de C#

j'ai réussi à le faire fonctionner dans l'autre sens dans Visual Studio en ayant deux projets dans la même solution, et en ajoutant une référence du code C# pour le projet F #. Après cela, je pourrais appeler du code C# et même le franchir pendant le débogage.

Ce que je suis en train de faire est de code F # DE C# au lieu du code C# de F #. J'ai ajouté une référence au projet F # au projet C#, mais cela ne fonctionne pas comme avant. Je voudrais savoir si c'est possible sans le faire manuellement.

+3

Sauf si vous avez des questions spécifiques, l'ajout d'une référence à un projet F # d'un C# un aujourd'hui « fonctionne ". Il n'y a rien d'extraordinaire ici, car c'est l'une des promesses ou des avantages fondamentaux de l'architecture .NET (Language agnostic, MSIL, etc.). En fait, le contraire serait bizarre. Qu'attendez-vous de plus pour cette générosité? –

Répondre

43

(EDIT: J'avais à l'origine lié à ces fichiers en externe, mais mon hôte SVN n'autorise plus l'accès anonyme, donc le code est maintenant en ligne dans cette réponse r.)

est Ci-dessous un exemple de travail d'appeler F # de C#.

Comme vous avez rencontré, je ne pouvais ajouter une référence en sélectionnant l'onglet « Ajouter une référence ... Projets ». Au lieu de cela, j'ai dû le faire manuellement, en naviguant vers l'assemblage F # dans l'onglet "Ajouter une référence ... Parcourir".

------ 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# ESSAIS DE L'UNITÉ POUR LE 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); 
     } 

    } 
} 
+0

Merci. "J'ai dû le faire manuellement, en accédant à l'assemblage F # dans l'onglet" Ajouter une référence ... Parcourir "." est ce qui a fonctionné pour moi. – ZeroKelvin

2

De this link ils semblent avoir un certain nombre de solutions possibles, mais celui qui semblait le plus simple était this comment:

F # Code:

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# Code:

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

je reçois une erreur sur la ligne val: val f3: FCallback -> int -> int -> int «. Erreur mot-clé inattendue « val » dans la définition construction attendue incomplète structurée ou avant ce moment ou un autre jeton » –

18

Il devrait ' il suffit de travailler », bien que vous deviez construire le projet F # avant que la référence projet-projet de C# ne fonctionne (j'oublie).

Une source commune des questions/modules est des espaces de noms. Si votre code F # ne commence pas par une déclaration d'espace de noms, il est placé dans un module avec le même nom que le nom de fichier, de sorte que par exemple. à partir de C# votre type peut apparaître comme "Program.Foo" plutôt que simplement "Foo" (si Foo est un type F # défini dans Program.fs).

+1

Merci pour les informations concernant le nom du module :). – ZeroKelvin

+2

Ouais je besoin de blog que l'un, il provoque beaucoup de confusion. – Brian

+0

Un problème supplémentaire est déclenchée, lorsque le projet FSharp (générateur de la référence dll) se trouve dans la même solution avec le Csharp (projet de consommation) –

2

// 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); 
Questions connexes