简体   繁体   English

C#中的F#扩展方法

[英]F# extension methods in C#

If you were to define some extension methods, properties in an assembly written in F#, and then use that assembly in C#, would you see the defined extensions in C#?如果您要在 F# 中编写的程序集中定义一些扩展方法、属性,然后在 C# 中使用该程序集,您会在 C# 中看到定义的扩展吗?

If so, that would be so cool.如果是这样,那就太酷了。

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

This method could be used in C# only by adding the namespace (using using) to the file where it will be used.这种方法可以在 C# 中使用,只需将命名空间(使用 using)添加到将要使用的文件中。

if (p2.Description.Exists()) {   ...}

Here is a link to the original blogpost. 这是原始博客文章的链接。

Answering question in comments "Extension Static Methods":在评论“扩展 Static 方法”中回答问题:

namespace ExtensionFSharp 

module CollectionExtensions = 

  type System.Linq.Enumerable with   
    static member RangeChar(first:char, last:char) = 
      {first .. last}

In F# you call it like so:在 F# 你这样称呼它:

open System.Linq 
open ExtensionFSharp.CollectionExtensions 

let rangeChar = Enumerable.RangeChar('a', 'z') 
printfn "Contains %i items" rangeChar.CountItems

In C# you call it like so:在 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);
            }
        }
    }

Now, give me my freaking medal!现在,把我那该死的奖牌给我!

Despite my other answer, I did just try this with the F# CTP (on VS shell) and C# Express from my box at home (all free dev tools,): and this works:尽管我有其他答案,但我还是在家里的盒子里用 F# CTP(在 VS shell 上)和 C# Express 尝试了这个(所有免费的开发工具,):这有效:

F# 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# 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
    }
}

I was not aware you could do this;我不知道你可以这样做; nifty.漂亮。 Credit to @alex.感谢@alex。

Per the language spec , section 10.7 "Type extensions":根据语言规范,第 10.7 节“类型扩展”:

Optional extension members are syntactic sugar for static members.可选的扩展成员是 static 成员的语法糖。 Uses of optional extension members elaborate to calls to static members with encoded names where the object is passed as the first argument.可选扩展成员的使用详细说明了对具有编码名称的 static 成员的调用,其中 object 作为第一个参数传递。 The encoding of names is not specified in this release of F# and is not compatible with C# encodings of C# extension members此版本 F# 中未指定名称的编码,并且与 C# 扩展成员的 C# 编码不兼容

For some reason, the accepted answer suggests using reflection to get an F# type extension method.出于某种原因,接受的答案建议使用反射来获得 F# 类型扩展方法。 Since the compiled method name is different between versions of F#, and may be different depending on arguments, inlining and other naming related issues, I would rather suggest using CompiledNameAttribute instead, which is much easier and blends in easily with C#.由于 F# 版本之间的编译方法名称不同,并且可能因 arguments、内联和其他命名相关问题而有所不同,因此我宁愿建议使用CompiledNameAttribute ,它更容易与 ZD7EFA19FBE7D397D74Z5ADB 融合。 Besides, no reflection (and its performance and type safety issues) necessary.此外,不需要反射(及其性能和类型安全问题)。

Suppose you have this in F#:假设你在 F# 中有这个:

namespace Foo.Bar
module StringExt =
    type System.String with
        static member ToInteger s = System.Int64.Parse(s)

You wouldn't be able to call this directly and the compiled version would look like this (depending on whether or not there are overloads):您将无法直接调用它,编译后的版本将如下所示(取决于是否存在重载):

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

Unless you would use reflection, you can't access the method String.ToInteger.Static .除非您使用反射,否则您无法访问方法String.ToInteger.Static However, a simple method decoration with the CompiledNameAttribute solves this problem:然而,一个带有CompiledNameAttribute的简单方法装饰解决了这个问题:

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

Now the compiled method looks like this in Reflector, mark the change in the compiled name:现在编译的方法在 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);
    }
}

You can still use this method the way you are used to in F# (as String.ToInteger in this case).您仍然可以按照在 F# 中使用的方式使用此方法(在本例中为String.ToInteger )。 But more importantly, you can now use this method without reflection or other trickery from C#:但更重要的是,您现在可以使用此方法而无需 C# 中的反射或其他技巧:

var int x = Foo.Bar.StringExt.ToInteger("123");

And of course, you can make your life simpler by adding a type alias in C# for the Foo.Bar.StringExt module:当然,您可以通过在 C# 中为Foo.Bar.StringExt模块添加类型别名来简化您的生活:

using Ext = Foo.Bar.StringExt;
....
var int x = Ext.ToInteger("123");

This is not the same as an extension method, and decorating the static member with a System.Runtime.CompilerServices.Extension attribute gets ignored.这与扩展方法不同,使用System.Runtime.CompilerServices.Extension属性装饰 static 成员将被忽略。 This is merely a simple way to use type extensions from other .NET languages.这只是使用其他 .NET 语言的类型扩展的简单方法。 If you want a "genuine" extension method that appears to act on the type, use the let -syntax from the other answers here.如果您想要一个似乎对类型起作用的“真正”扩展方法,请使用此处其他答案中的let -syntax。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM