extended - how to add extension method in c#
Métodos de extensión F#en C# (4)
A pesar de mi otra respuesta, simplemente intenté esto con el F # CTP (en VS shell) y C # Express desde mi caja en casa (¡todas las herramientas de desarrollo gratuitas!), 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()
DO#
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
}
}
No sabía que podrías hacer esto; hábil. Crédito a @alex.
Si definiera algunos métodos de extensión, propiedades en un ensamblado escrito en F # y luego use ese ensamblaje en C #, ¿vería las extensiones definidas en C #?
Si es así, sería genial.
Por alguna razón, la respuesta aceptada sugiere usar la reflexión para obtener un método de extensión de tipo F #. Como 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).
Supongamos que tienes esto en F #:
namespace Foo.Bar
module StringExt =
type System.String with
static member ToInteger s = System.Int64.Parse(s)
No podría llamar esto directamente 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 el reflejo, no puede acceder al método String.ToInteger.Static
. Sin embargo, una decoración de método simple con 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 se ve así en Reflector, marque 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);
}
}
Todavía puede usar este método de la forma en que está acostumbrado en F # (como String.ToInteger
en este caso). Pero más importante aún, ahora puedes usar este método sin reflejos u otros engaños de C #:
var int x = Foo.Bar.StringExt.ToInteger("123");
Y por supuesto, puede simplificar su vida agregando 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 extensiones de tipo de otros lenguajes .NET. Si desea un método de extensión "genuino" que parezca actuar según el tipo, use la sintaxis let
de las otras respuestas aquí.
Según la especificación del idioma , sección 10.7 "Extensiones de tipo":
Los miembros de extensión opcionales son azúcar sintáctico para 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 los nombres no se especifica en esta versión de F # y no es compatible con las codificaciones C # de los miembros de la extensión C #
[<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 se puede usar en C # solo agregando el espacio de nombres (usando using) al archivo donde se usará.
if (p2.Description.Exists()) { ...}
Aquí hay un enlace al blogpost original.
Respuesta a la pregunta en los comentarios "Métodos estáticos de extensión":
namespace ExtensionFSharp
module CollectionExtensions =
type System.Linq.Enumerable with
static member RangeChar(first:char, last:char) =
{first .. last}
En F # lo llamas así:
open System.Linq
open ExtensionFSharp.CollectionExtensions
let rangeChar = Enumerable.RangeChar(''a'', ''z'')
printfn "Contains %i items" rangeChar.CountItems
En C # lo llamas así:
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!