2

I have a problem I have a simple enum with description attribute in c#

public enum Service 
{
    [Description("Unknown")]
    Unknown = 0
}

No I have extension in f# that will get me that description and it looks like:

[<Extension>]
static member inline GetEnumDescription(enum:'TEnum when 'TEnum :> Enum) : string =      

    try
        let attributes : seq<DescriptionAttribute[]> = enum.GetType().GetField(enum.ToString()).GetCustomAttributes(typedefof<DescriptionAttribute>, false) |> Seq.cast<DescriptionAttribute[]>             

        match attributes |> Seq.length > 0 with
            | true -> 
                let attribute : DescriptionAttribute = enum |> Seq.head
                attribute.Description
            | _ -> enum.ToString()
    with 
        | :? EnumException as ex -> 
            EnumExtensions._logger.Error(sprintf "Exception in getting enum description - %s" ex.Message)
            enum.ToString()

So meta in c# looks like:

 [CompilationMapping(SourceConstructFlags.ObjectType)]
public class EnumExtensions
{
    public EnumExtensions();

    public static string GetEnumDescription<TEnum>(this TEnum @enum) where TEnum : Enum, IEnumerable<DescriptionAttribute>;
}

Now when i try using this in c# calling :

public string Description => Service.GetEnumDescription(); //Service is set to Unknown enum value

I am getting something like:

Error CS0315 The type 'Enums.Service' cannot be used as type parameter 'TEnum' in the generic type or method 'EnumExtensions.GetEnumDescription(TEnum)'. There is no boxing conversion from 'Enums.Service' to 'System.Collections.Generic.IEnumerable'.

I am lost at this.

Wojciech Szabowicz
  • 3,646
  • 5
  • 43
  • 87
  • 3
    That C# meta doesn't make sense. A type cannot be an `Enum` and also an `IEnumerable`. – DavidG Jul 05 '19 at 11:35
  • Aye I've rebuild that project couple of time and always ended with this – Wojciech Szabowicz Jul 05 '19 at 11:40
  • I don't know enough about F# nor F# interop to help beyond that I'm afraid. – DavidG Jul 05 '19 at 11:42
  • What does this line: `let attribute : DescriptionAttribute = enum |> Seq.head` do? To me it looks like you're trying to treat `enum` as a `seq` with a head and all. – V0ldek Jul 05 '19 at 12:09
  • Get first description attribute from enum, as there can be many description attributes from let attributes : seq = enum.GetType().GetField(enum.ToString()).GetCustomAttributes(typedefof, false) |> Seq.cast – Wojciech Szabowicz Jul 05 '19 at 12:18
  • But `enum` is not a sequence. You can't `Seq.head` it. – V0ldek Jul 05 '19 at 12:26
  • [this answer](https://stackoverflow.com/a/8086788) to [Create Generic method constraining T to an Enum](https://stackoverflow.com/q/79126) shows how to create an `enum` constraint in f#. Scroll down to **F# Solution as alternative**. However, in the same answer it's noted that c# has no `enum` constraint prior to 7.3. Maybe that's why the "c# meta" looks wrong? – dbc Jul 05 '19 at 18:13

2 Answers2

2

As far as I can see, here:

let attribute : DescriptionAttribute = enum |> Seq.head

you're treating enum like a sequence, trying to extract its head. That causes F# to expect enum to be of a sequence type, and thus the emitted C# meta has the IEnumerable<DescriptionAttribute> constraint on TEnum. I guess the line should be

let attribute : DescriptionAttribute = attributes |> Seq.head
V0ldek
  • 9,623
  • 1
  • 26
  • 57
2

I can't get it to work either, but you can rewrite the F# method to:

[<Extension>]
type Extensions =
    [<Extension>]
    static member inline GetEnumDescription(enum:'TEnum when 'TEnum :> Enum) : string =      

        let attributes = enum.GetType().GetField(enum.ToString()).GetCustomAttributes(typeof<DescriptionAttribute>, false)
        match attributes.Length with
        | x when x > 0 -> attributes.[0] |> (fun a -> a :?> DescriptionAttribute) |> fun a -> a.Description
        | _ -> raise (InvalidOperationException("DescriptionAttribute is missing"))

You'll then get the Description property from the DescriptionAttributes when calling from C#:

  string result = Service.Unknown.GetEnumDescription();
  Console.WriteLine(result);