How do I get ToString () to return an Enum attribute description if it exists?

I just found a question like this in C # and converted some code to F #, but unfortunately it was still returning that name. The question is, where is my mistake? or are there alternative ways to have some enum structure with an optional returnin string when I want to get ABToString ()

Here's my attempt:

[<TypeConverter(typedefof<EnumToStringUsingDescription>)>]
type FontVariant =
    | [<Description("small-caps")>] smallCaps = 0

      

and

type EnumToStringUsingDescription() =
    inherit TypeConverter()
    override X.CanConvertFrom(context : ITypeDescriptorContext, sourceType : Type) =
        sourceType.Equals(typedefof<Enum>);
    override X.CanConvertTo(context : ITypeDescriptorContext, destinationType : Type) =
        (destinationType.Equals(typedefof<String>));
    override X.ConvertFrom(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj) =
        base.ConvertFrom(context, culture, value);
    override X.ConvertTo(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj, destinationType : Type) =
        if (not <| destinationType.Equals(typedefof<String>)) then
            raise <| new ArgumentException("Can only convert to string.", "destinationType");

        if (not <| value.GetType().BaseType.Equals(typedefof<Enum>)) then
            raise <| new ArgumentException("Can only convert an instance of enum.", "value");

        let name = value.ToString();
        let attrs = 
            value.GetType().GetField(name).GetCustomAttributes(typedefof<DescriptionAttribute>, false);
        if (attrs.Length > 0) then attrs.[0]
        else value

      

+3


source to share


2 answers


There are a couple of problems here. First, adding a TypeConverter won't affect the .ToString()

. Second, your transformation is returning an attribute, not a description in the attribute. This is where the function works.

let getEnumDescription (value: Enum) =
   let typ = value.GetType()
   let name = value.ToString();
   let attrs = typ.GetField(name).GetCustomAttributes(typedefof<DescriptionAttribute>, false)
   if (attrs.Length > 0) then (attrs.[0] :?> DescriptionAttribute).Description :> obj
   else name :> obj

      



However, some libraries / frameworks will use a type converter if available. It might seem like something like this. Maybe you need to implement ConvertFrom / CanConvertFrom too, I'm not sure.

type EnumToStringUsingDescription() =
    inherit TypeConverter()
    override X.CanConvertTo(context : ITypeDescriptorContext, destinationType : Type) = (destinationType.Equals(typedefof<String>))
    override X.ConvertTo(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj, destinationType : Type) =
       let typ = value.GetType()
       if (not <| typ.IsEnum) then raise <| new ArgumentException("Can only convert from enum.", "value");
       if (not <| typ.Equals typeof<string>) then raise <| new ArgumentException("Can only convert to string.", "destinationType");
       getEnumDescription (value :?> Enum)

      

+5


source


Since, as Robert said, enums cannot have members, and therefore you cannot override ToString

, you can do something like this as a kind of compromise:

type FontVariant =
  | ``small-caps`` = 0

      

Then printf

works as desired:

printfn "%A" FontVariant.``small-caps``

      

> small-caps

Also, John's suggestion to use a discriminatory union is good. They look exactly like enums, minus numeric values:



type FontVariant =
  | SmallCaps
  override this.ToString() =
    match this with
    | SmallCaps -> "small-caps"

      

Use format %O

( %A

will use Reflection and print the case name).

printfn "%O" FontVariant.SmallCaps

      

If you need a numeric value like the enums are listed, you can define a property:

member this.Value =
  match this with
  | SmallCaps -> 0

printfn "%d" FontVariant.SmallCaps.Value

      

+5


source







All Articles