如果在使用F#编写的程序集中定义了一些扩展方法和属性,然后在C#中使用该程序集,您是否会在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
只需在将要使用该方法的文件中添加命名空间(使用using)即可在C#中使用此方法。
if (p2.Description.Exists()) { ...}
回答评论中的问题“扩展静态方法”:
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);
}
}
}
现在,把我的奖章给我!
CompiledNameAttribute
来更简单、类型安全地从 C# 调用 F#。 - Abel#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。
由于某些原因,被接受的答案建议使用反射来获取F#类型扩展方法。由于编译后的方法名称在F#版本之间可能不同,也可能因参数、内联和其他命名相关问题而不同,所以我更建议使用CompiledNameAttribute
,这样做更容易并且与C#混合得更好。此外,无需反射(及其性能和类型安全问题)。
假设你有以下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
属性装饰静态成员将被忽略。这只是一种简单的方式,可以从其他 .NET 语言中使用类型扩展。如果你想要一个看起来像作用于类型的“真正的”扩展方法,请使用此处其他答案中的let
语法。