Если вы должны были определить некоторые методы расширения, свойства в сборке, написанные в F #, а затем использовать эту сборку в С#, вы увидите определенные расширения в С#?
Если это так, это будет так здорово.
Если вы должны были определить некоторые методы расширения, свойства в сборке, написанные в F #, а затем использовать эту сборку в С#, вы увидите определенные расширения в С#?
Если это так, это будет так здорово.
[<System.Runtime.CompilerServices.Extension>]
module Methods =
[<System.Runtime.CompilerServices.Extension>]
let Exists(opt : string option) =
match opt with
| Some _ -> true
| None -> false
Этот метод может использоваться в С# только путем добавления пространства имен (с использованием) в файл, в котором он будет использоваться.
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
В С# вы вызываете его так:
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) и С# 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()
С#
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 "Расширения типа":
Дополнительные члены расширения - это синтаксический сахар для статических членов. Использование необязательных членов расширения разрабатывается для вызовов статических членов с закодированными именами, где объект передается в качестве первого аргумента. Кодировка имен не указана в этой версии F # и несовместима с кодировками С# членов расширения С#.
По какой-то причине в принятом ответе предлагается использовать рефлексию для получения метода расширения типа F #. Поскольку имя скомпилированного метода отличается от версий F # и может отличаться в зависимости от аргументов, встраивания и других проблем, связанных с именами, я бы предпочел использовать CompiledNameAttribute
, что намного проще и легко сочетается с С#. Кроме того, не требуется никакого отражения (и его проблем с производительностью и типом).
Предположим, что у вас есть это в 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
). Но что более важно, теперь вы можете использовать этот метод без отражения или другого обмана с С#:
var int x = Foo.Bar.StringExt.ToInteger("123");
И, конечно, вы можете сделать свою жизнь проще, добавив псевдоним типа в С# для модуля Foo.Bar.StringExt
:
using Ext = Foo.Bar.StringExt;
....
var int x = Ext.ToInteger("123");
Это не то же самое, что и метод расширения, и игнорирование элемента static с атрибутом System.Runtime.CompilerServices.Extension
. Это просто простой способ использовать типы расширений на других языках .NET. Если вам нужен "подлинный" метод расширения, который, как представляется, действует на тип, используйте let
-syntax из других ответов здесь.