簡體   English   中英

C#中的F#擴展方法

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM