F# extension methods in C#

后端 未结 4 1037
死守一世寂寞
死守一世寂寞 2020-12-02 10:23

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#?

4条回答
  •  庸人自扰
    2020-12-02 11:20

    For some reason, the accepted answer suggests using reflection to get an F# type extension method. 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#. Besides, no reflection (and its performance and type safety issues) necessary.

    Suppose you have this in 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. However, a simple method decoration with the CompiledNameAttribute solves this problem:

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

    Now the compiled method looks like this in Reflector, mark the change in the compiled name:

    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). But more importantly, you can now use this method without reflection or other trickery from 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:

    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. This is merely a simple way to use type extensions from other .NET languages. If you want a "genuine" extension method that appears to act on the type, use the let-syntax from the other answers here.

提交回复
热议问题