如果你要定义一些扩展方法,用F#编写的程序集中的属性,然后在C#中使用该程序集,你会在C#中看到已定义的扩展吗?
如果是这样,那就太酷了。
答案 0 :(得分:47)
[<System.Runtime.CompilerServices.Extension>]
module Methods =
[<System.Runtime.CompilerServices.Extension>]
let Exists(opt : string option) =
match opt with
| Some _ -> true
| None -> false
只有通过将名称空间(使用使用)添加到将使用它的文件中,才能在C#中使用此方法。
if (p2.Description.Exists()) { ...}
Here is a link to the original blogpost.
在评论中回答问题“扩展静态方法”:
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);
}
}
}
现在,给我一把惊人的奖章!
答案 1 :(得分:15)
尽管我得到了其他答案,我只是尝试使用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。
答案 2 :(得分:6)
根据language spec,第10.7节“类型扩展名”:
可选的扩展成员是静态成员的语法糖。可选扩展成员的使用精心调用具有编码名称的静态成员,其中对象作为第一个参数传递。此版本的F#中未指定名称编码,并且与C#扩展成员的C#编码不兼容
答案 3 :(得分:4)
由于某种原因,接受的答案建议使用反射来获得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语言中使用type extensions的简单方法。如果您想要一个看似对该类型起作用的“正版”扩展方法,请使用此处其他答案中的let
- 语法。