C#中的F#扩展方法

Joa*_*nge 35 c# extension-methods f#

如果要定义一些扩展方法,用F#编写的程序集中的属性,然后在C#中使用该程序集,您会在C#中看到已定义的扩展吗?

如果是这样,那就太酷了.

ale*_*lex 48

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

只有通过将命名空间(使用using)添加到将使用它的文件中,才能在C#中使用此方法.

if (p2.Description.Exists()) {   ...}
Run Code Online (Sandbox Code Playgroud)

这是原始博客帖子的链接.

在评论"扩展静态方法"中回答问题:

namespace ExtensionFSharp 

module CollectionExtensions = 

  type System.Linq.Enumerable with   
    static member RangeChar(first:char, last:char) = 
      {first .. last}
Run Code Online (Sandbox Code Playgroud)

在F#中,您可以这样称呼它:

open System.Linq 
open ExtensionFSharp.CollectionExtensions 

let rangeChar = Enumerable.RangeChar('a', 'z') 
printfn "Contains %i items" rangeChar.CountItems
Run Code Online (Sandbox Code Playgroud)

在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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在,给我一个惊人的勋章!

  • 你绝对应该因为反思的创造性方式而获得一枚奖牌,也许在 2009 年这是唯一的方式。但是,您现在可以使用 [`CompiledNameAttribute`](http://stackoverflow.com/a/39678801/111575) 获得更简单且类型安全的从 C# 调用 F# 的方法。 (2认同)

Bri*_*ian 16

尽管我的另一个答案是,我只是尝试使用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()        
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道你能做到这一点; 俏皮.感谢@alex.


Abe*_*bel 8

出于某种原因,已接受的答案建议使用反射来获取 F# 类型的扩展方法。由于 F# 版本之间编译的方法名称不同,并且可能因参数、内联和其他与命名相关的问题而异,我宁愿建议使用CompiledNameAttribute它,这样更容易,并且很容易与 C# 融合。此外,不需要反射(及其性能和类型安全问题)。

假设你在 F# 中有这个:

namespace Foo.Bar
module StringExt =
    type System.String with
        static member ToInteger s = System.Int64.Parse(s)
Run Code Online (Sandbox Code Playgroud)

您将无法直接调用它,编译后的版本将如下所示(取决于是否存在重载):

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

除非您使用反射,否则您无法访问该方法String.ToInteger.Static。然而,一个简单的方法装饰CompiledNameAttribute解决了这个问题:

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

现在编译的方法在 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您仍然可以按照在 F# 中习惯的方式使用此方法(如String.ToInteger本例中所示)。但更重要的是,您现在可以使用此方法而无需反射或 C# 中的其他技巧:

var int x = Foo.Bar.StringExt.ToInteger("123");
Run Code Online (Sandbox Code Playgroud)

当然,您可以通过在 C# 中为Foo.Bar.StringExt模块添加类型别名来使您的生活更简单:

using Ext = Foo.Bar.StringExt;
....
var int x = Ext.ToInteger("123");
Run Code Online (Sandbox Code Playgroud)

这与扩展方法不同,使用System.Runtime.CompilerServices.Extension属性装饰静态成员会被忽略。这只是使用来自其他 .NET 语言的类型扩展的一种简单方法。如果您想要一个似乎对类型起作用的“真正”扩展方法,请使用let此处其他答案中的-syntax。


Bri*_*ian 6

根据语言规范,第10.7节"类型扩展":

可选的扩展成员是静态成员的语法糖.可选扩展成员的使用精心调用具有编码名称的静态成员,其中对象作为第一个参数传递.此版本的F#中未指定名称编码,并且与C#扩展成员的C#编码不兼容