2009-03-31 17 views
31

Si definiera algunos métodos de extensión, propiedades en un ensamblado escrito en F # y luego use ese ensamblaje en C#, ¿verá las extensiones definidas en C#?Métodos de extensión F # en C#

Si es así, sería genial.

+1

Véase también http://cs.hubfs.net/forums/thread/13967.aspx – Brian

Respuesta

45
[<System.Runtime.CompilerServices.Extension>] 
module Methods = 
    [<System.Runtime.CompilerServices.Extension>] 
    let Exists(opt : string option) =     
    match opt with 
     | Some _ -> true     
     | None -> false 

Este método podría ser utilizado en C# sólo mediante la adición del espacio de nombres (con el uso) para el archivo donde se va a utilizar.

if (p2.Description.Exists()) { ...} 

Here is a link to the original blogpost.

Respondiendo a la pregunta en los comentarios "Extensión métodos estáticos":

namespace ExtensionFSharp 

module CollectionExtensions = 

    type System.Linq.Enumerable with 
    static member RangeChar(first:char, last:char) = 
     {first .. last} 

En F # que lo llaman así:

open System.Linq 
open ExtensionFSharp.CollectionExtensions 

let rangeChar = Enumerable.RangeChar('a', 'z') 
printfn "Contains %i items" rangeChar.CountItems 

En C# se llame como entonces:

using System; 
using System.Collections.Generic; 
using ExtensionFSharp; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var method = typeof (CollectionExtensions).GetMethod("Enumerable.RangeChar.2.static"); 


      var rangeChar = (IEnumerable<char>) method.Invoke(null, new object[] {'a', 'z'}); 
      foreach (var c in rangeChar) 
      { 
       Console.WriteLine(c); 
      } 
     } 
    } 

¡Ahora, dame mi maldita medalla!

+0

Gracias, pero ¿qué pasa con las propiedades de extensión de la instancia y los métodos estáticos, y las propiedades de extensión estática? ¿Serían llamados también? La razón por la que pregunté esto es porque, sé que C# solo tiene métodos de extensión de instancias. –

+0

Gracias, acabo de ver su actualización. –

+0

Definitivamente se merece una medalla por la creatividad con la reflexión y quizás en 2009 esa fue la única forma. Sin embargo, ahora puede usar ['CompiledNameAttribute'] (http://stackoverflow.com/a/39678801/111575) para un enfoque mucho más simple y seguro para llamar a F # desde C#. – Abel

6

por los language spec, sección 10.7 "Extensiones" Tipo:

miembros de extensión opcionales son el azúcar sintáctica para los miembros estáticos. Los usos de los miembros de extensiones opcionales se complementan con llamadas a miembros estáticos con nombres codificados donde el objeto se pasa como primer argumento. La codificación de nombres no está especificada en esta versión de F # y no es compatible con las codificaciones C# de los miembros de extensión C#

+0

¿Sabes si hay planes para unificar las formas de F # y C# de implementar extensiones de tipo. Sería bueno si uno no necesita usar los atributos adicionales. – Joh

+0

No parece que haremos esto para la primera versión de F # (por ejemplo, no en el marco temporal VS2010). – Brian

14

A pesar de mi otra respuesta, simplemente intenté esto con F # CTP (en VS shell) y C# Express desde mi caja en casa (! todas las herramientas dev libres), y esto funciona:

F #

#light 
namespace MyFSharp 

// C# way 
[<System.Runtime.CompilerServices.Extension>] 
module ExtensionMethods = 
    [<System.Runtime.CompilerServices.Extension>] 
    let Great(s : System.String) = "Great" 

    // F# way 
    type System.String with 
     member this.Awesome() = "Awesome" 
    let example = "foo".Awesome()   

C#

using System; 
using MyFSharp; // reference the F# dll 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var s = "foo"; 
     //s.Awesome(); // no 
     Console.WriteLine(s.Great()); // yes 
    } 
} 

yo no era consciente de que podría hacer esto; hábil. Crédito a @alex.

+1

Gracias, ¿pero no es esto solo un método de extensión de instancia? –

+0

Gracias, estaba más interesado en que otras extensiones de F # aparezcan en C#, pero su respuesta de otro tipo aborda eso. –

3

Por alguna razón, la respuesta aceptada sugiere el uso de la reflexión para obtener un método de extensión de tipo F #. Dado que el nombre del método compilado es diferente entre las versiones de F #, y puede ser diferente según los argumentos, la alineación y otros problemas relacionados con los nombres, prefiero usar CompiledNameAttribute, que es mucho más fácil y se combina fácilmente con C#. Además, no es necesario reflexionar (y su rendimiento y tipo de problemas de seguridad).

Suponga que tiene esto en C#:

namespace Foo.Bar 
module StringExt = 
    type System.String with 
     static member ToInteger s = System.Int64.Parse(s) 

No sería capaz de llamar a esta directa y la versión compilada se vería así (dependiendo de si hay o no sobrecargas):

namespace Foo.Bar 
{ 
    using Microsoft.FSharp.Core; 
    using System; 

    [CompilationMapping(SourceConstructFlags.Module)] 
    public static class StringExt 
    { 
     public static long String.ToInteger.Static(string s) => 
      long.Parse(s); 
    } 
} 

A menos que use la reflexión, no puede acceder al método String.ToInteger.Static.Sin embargo, una decoración simple método con el CompiledNameAttribute resuelve este problema:

namespace Foo.Bar 
module StringExt = 
    type System.String with 
     [<CompiledName("ToInteger")>] 
     static member ToInteger s = System.Int64.Parse(s) 

Ahora el método compilado es la siguiente con reflector, marcan el cambio en el nombre compilado:

namespace Foo.Bar 
{ 
    using Microsoft.FSharp.Core; 
    using System; 

    [CompilationMapping(SourceConstructFlags.Module)] 
    public static class StringExt 
    { 
     [CompilationSourceName("ToInteger")] 
     public static long ToInteger(string s) => 
      long.Parse(s); 
    } 
} 

puede seguir utilizando este método de la forma en que está acostumbrado en F # (como String.ToInteger en este caso). Pero lo más importante, ahora se puede utilizar este método sin reflexión o de otro engaño de C#:

var int x = Foo.Bar.StringExt.ToInteger("123"); 

Y, por supuesto, usted puede hacer su vida más fácil mediante la adición de un alias de tipo en C# para el módulo Foo.Bar.StringExt:

using Ext = Foo.Bar.StringExt; 
.... 
var int x = Ext.ToInteger("123"); 

Esto no es lo mismo que un método de extensión, y se ignora la decoración del miembro estático con un atributo System.Runtime.CompilerServices.Extension. Esta es simplemente una forma sencilla de usar type extensions desde otros lenguajes .NET. Si desea un método de extensión "genuino" que parezca actuar según el tipo, use la let -sintaxis de las otras respuestas aquí.

Cuestiones relacionadas