[英]F# extension methods in C#
如果您要在 F# 中编写的程序集中定义一些扩展方法、属性,然后在 C# 中使用该程序集,您会在 C# 中看到定义的扩展吗?
如果是这样,那就太酷了。
[<System.Runtime.CompilerServices.Extension>]
module Methods =
[<System.Runtime.CompilerServices.Extension>]
let Exists(opt : string option) =
match opt with
| Some _ -> true
| None -> false
这种方法可以在 C# 中使用,只需将命名空间(使用 using)添加到将要使用的文件中。
if (p2.Description.Exists()) { ...}
在评论“扩展 Static 方法”中回答问题:
namespace ExtensionFSharp
module CollectionExtensions =
type System.Linq.Enumerable with
static member RangeChar(first:char, last:char) =
{first .. last}
在 F# 你这样称呼它:
open System.Linq
open ExtensionFSharp.CollectionExtensions
let rangeChar = Enumerable.RangeChar('a', 'z')
printfn "Contains %i items" rangeChar.CountItems
在 C# 你这样称呼它:
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);
}
}
}
现在,把我那该死的奖牌给我!
尽管我有其他答案,但我还是在家里的盒子里用 F# CTP(在 VS shell 上)和 C# Express 尝试了这个(所有免费的开发工具,):这有效:
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
}
}
我不知道你可以这样做; 漂亮。 感谢@alex。
根据语言规范,第 10.7 节“类型扩展”:
可选的扩展成员是 static 成员的语法糖。 可选扩展成员的使用详细说明了对具有编码名称的 static 成员的调用,其中 object 作为第一个参数传递。 此版本 F# 中未指定名称的编码,并且与 C# 扩展成员的 C# 编码不兼容
出于某种原因,接受的答案建议使用反射来获得 F# 类型扩展方法。 由于 F# 版本之间的编译方法名称不同,并且可能因 arguments、内联和其他命名相关问题而有所不同,因此我宁愿建议使用CompiledNameAttribute
,它更容易与 ZD7EFA19FBE7D397D74Z5ADB 融合。 此外,不需要反射(及其性能和类型安全问题)。
假设你在 F# 中有这个:
namespace Foo.Bar
module StringExt =
type System.String with
static member ToInteger s = System.Int64.Parse(s)
您将无法直接调用它,编译后的版本将如下所示(取决于是否存在重载):
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);
}
}
除非您使用反射,否则您无法访问方法String.ToInteger.Static
。 然而,一个带有CompiledNameAttribute
的简单方法装饰解决了这个问题:
namespace Foo.Bar
module StringExt =
type System.String with
[<CompiledName("ToInteger")>]
static member ToInteger s = System.Int64.Parse(s)
现在编译的方法在 Reflector 中是这样的,标记编译名称的变化:
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);
}
}
您仍然可以按照在 F# 中使用的方式使用此方法(在本例中为String.ToInteger
)。 但更重要的是,您现在可以使用此方法而无需 C# 中的反射或其他技巧:
var int x = Foo.Bar.StringExt.ToInteger("123");
当然,您可以通过在 C# 中为Foo.Bar.StringExt
模块添加类型别名来简化您的生活:
using Ext = Foo.Bar.StringExt;
....
var int x = Ext.ToInteger("123");
这与扩展方法不同,使用System.Runtime.CompilerServices.Extension
属性装饰 static 成员将被忽略。 这只是使用其他 .NET 语言的类型扩展的简单方法。 如果您想要一个似乎对类型起作用的“真正”扩展方法,请使用此处其他答案中的let
-syntax。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.