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
source to share
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)
source to share
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
source to share