Методы расширения F # в C # - PullRequest
34 голосов
/ 31 марта 2009

Если бы вы определили некоторые методы расширения, свойства в сборке, написанной на F #, а затем использовали эту сборку в C #, вы бы увидели определенные расширения в C #?

Если это так, это было бы так здорово.

Ответы [ 4 ]

47 голосов
/ 31 марта 2009
[<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()) {   ...}

Вот ссылка на оригинальный пост в блоге.

Ответ на вопрос в комментариях "Расширение статических методов":

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);
            }
        }
    }

А теперь дай мне мою чертову медаль!

15 голосов
/ 01 апреля 2009

Несмотря на мой другой ответ, я просто попробовал это с F # CTP (на оболочке VS) и 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.

6 голосов
/ 01 апреля 2009

Согласно спецификации языка , раздел 10.7 «Расширения типов»:

Дополнительные элементы расширения являются синтаксическим сахаром для статических элементов. Использование опциональных расширенных членов упрощает обращение к статическим членам с закодированными именами, где объект передается в качестве первого аргумента. Кодировка имен не указана в этом выпуске F # и не совместима с C # кодировками членов расширения C #

4 голосов
/ 24 сентября 2016

По некоторым причинам принятый ответ предлагает использовать отражение, чтобы получить метод расширения типа 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 из других ответов здесь.

...