Extension methods for specific generic types

2020-08-20 15:47发布

问题:

I'm attempting to create various extension method for a generic type bound to specific generic type parameters in F#, but the language does not seem to be allowing me:

What I want to do is something like the following:

type IEnumerable<int> with
    member this.foo =
        this.ToString()

Yet it gives me the compiler error (underlining the int keyword):

Unexpected identifier in type name. Expected infix operator, quote symbol or other token.

The following does work, though it does not specifically bind the generic type parameter to int, as I want:

type IEnumerable<'a> with
    member this.foo =
        this.ToString()

Is there any way to accomplish this aim in F# - am I perhaps just using the wrong syntax? If not, I would appreciate if someone could suggest a workaround, perhaps using type constraints somewhere.

回答1:

This isn't possible in the current version of F#, unfortunately. See related question here.



回答2:

Generic extension methods are now available in F# 3.1:

open System.Runtime.CompilerServices
open System.Collections.Generic

[<Extension>]
type Utils () =
    [<Extension>]
    static member inline Abc(obj: IEnumerable<int>) = obj.ToString()

printfn "%A" ([1..10].Abc())


回答3:

Well, you can use constraints - but not with sealed types like int.

type IEnumerable<'a when 'a :> InheritableType> =
member this.Blah =
    this.ToString()

Hmm...



回答4:

In order to help others looking for similar solutions, here is an example showing how to use generic extension methods with type constraints. In the example below, there is a type constraint requiring that the type argument passed exposes a default constructor. This is done using the [<CLIMutable>] attribute applied to the Order record. Also, I'm constraing the result of the method to the type passed.

In order to use the extension method you have to specify the type you want to use. Note that I'm also extending a generic dictionary interface.

[<Extension>]
type ExtensionMethds () = 

    [<Extension>]
    static member inline toObject<'T when 'T: (new: unit -> 'T)> (dic: IDictionary<string,obj>): 'T =
        let instance = new 'T()
        // todo: set properties via reflection using the dictionary passed in
        instance


[<CLIMutable>]
type Order = {id: int}

let usage = 
    let dictionaryWithDataFromDb = dict ["id","1" :> obj] 
    let theOrder = dictionaryWithDataFromDb.toObject<Order>()
    theOrder