2009-01-25 10 views
54

Estoy jugando con F # y C#, y me gustaría llamar al código F # de C#.Llamar código F # de C#

Pude hacer que funcionara al revés en Visual Studio al tener dos proyectos en la misma solución y agregar una referencia del código C# al proyecto F #. Después de hacer esto, puedo llamar al código C# e incluso pasar por él mientras estoy depurando.

Lo que intento hacer es código F # DESDE C# en lugar del código C# de F #. Agregué una referencia al proyecto F # al proyecto C#, pero no está funcionando como lo hacía antes. Me gustaría saber si esto es posible sin hacerlo manualmente.

+3

A menos que tenga problemas específicos, añadiendo una referencia a un proyecto # F desde un C# una hoy "simplemente funciona ". No hay nada extraordinario aquí, ya que esa es una de las promesas o beneficios fundamentales de la arquitectura .NET (sin tener en cuenta el idioma, MSIL, etc.). De hecho, lo opuesto sería extraño. ¿Qué más esperas para esta recompensa? –

Respuesta

43

(EDITAR: Originalmente había vinculado estos archivos externamente, pero mi host SVN ya no permite el acceso anónimo. Por lo tanto, el código ahora está en línea en este answe r.)

A continuación se muestra un ejemplo práctico de cómo llamar a F # desde C#.

Como encontraste, no pude agregar una referencia seleccionando de la pestaña "Agregar referencia ... Proyectos". En cambio, tuve que hacerlo manualmente, navegando al ensamblado F # en la pestaña "Agregar referencia ... Buscar".

------ ----- F # MÓDULO

// 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# UNIDAD DE ENSAYOS PARA EL MÓDULO -----

// 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

Gracias. "Tuve que hacerlo manualmente, navegando al ensamblado F # en la pestaña 'Agregar referencia ... Buscar'". es lo que funcionó para mí – ZeroKelvin

2

De this link parece que tienen una serie de posibles soluciones, pero el que parecía el más simple era this comment:

F # Código:

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# Código:

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

Aparece un error en la línea val: val f3: FCallback -> int -> int -> int "Error Palabra clave inesperada 'val' en la definición. Constructo estructurado incompleto esperado en este punto u otro token u otro". –

18

Se debería " simplemente trabaja ", aunque es posible que tengas que construir el proyecto F # antes de que funcione una referencia de proyecto a proyecto de C# (se me olvida).

Una fuente común de problemas son los espacios de nombres/módulos. Si su código F # no comienza con una declaración de espacio de nombres, se coloca en un módulo con el mismo nombre que el nombre del archivo, de modo que, p. desde C# su tipo podría aparecer como "Program.Foo" en lugar de solo "Foo" (si Foo es un tipo F # definido en Program.fs).

+1

Gracias por la información sobre los nombres de los módulos :). – ZeroKelvin

+2

Sí, necesito bloguear, causa mucha confusión. – Brian

+0

Se desencadena un problema adicional cuando el proyecto Fsharp (generador de la referencia dll) está en la misma solución que el Csharp (proyecto de consumidor) –

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); 
Cuestiones relacionadas