In order to use Enum's in combination with strings, I implemented a StringEnum class based on https://stackoverflow.com/a/424414/1293385.
However I run into problems when I try to implement the suggested user-defined conversion operations.
The StringEnum class is defined as follows:
public abstract class StringEnum
{
private readonly String name;
private readonly int value;
protected static Dictionary<string, StringEnum> instances
= new Dictionary<string, StringEnum>();
protected StringEnum(int value, string name)
{
this.value = value;
this.name = name;
instances.Add(name.ToLower(), this);
}
public static explicit operator StringEnum(string name)
{
StringEnum se;
if (instances.TryGetValue(name.ToLower(), out se))
{
return se;
}
throw new InvalidCastException();
}
public override string ToString()
{
return name;
}
}
I use this class as a base like this:
public class DerivedStringEnum : StringEnum
{
public static readonly DerivedStringEnum EnumValue1
= new DerivedStringEnum (0, "EnumValue1");
public static readonly DerivedStringEnum EnumValue2
= new DerivedStringEnum (1, "EnumValue2");
private DerivedStringEnum (int value, string name) : base(value, name) { }
}
However when I try to cast it using
string s = "EnumValue1"
DerivedStringEnum e = (DerivedStringEnum) s;
An InvalidCastException is returned. Inspection of the code shows that the instances attribute of the StringEnum class is never filled.
Is there an easy way to fix this?
I prefer not to use C# attribute "magic" such as [StringValue("EnumValue1")].
Thanks!
You have to define an explicit cast operator on the derived class as well. The base class is not expected to know how to cast to a derived class.
Since operators are static they are not inherited - the explicit cast operator is only defined between string
and StringEnum
. You can do this rather ugly double-cast yourself:
DerivedStringEnum e = (DerivedStringEnum)(StringEnum)s
Or in your derived class you can put: (edited after @ili pointed out my own oversight)
public static explicit operator DerivedStringEnum(string name)
{
return (DerivedStringEnum)(StringEnum)name;
}
You don't need to add another operator. You've already identified the real problem:
Inspection of the code shows that the instances attribute of the StringEnum class is never filled.
That's because nothing is ever forcing the DerivedStringEnum
class to be initialized. You're never referring to anything which would force it to be initialized. If you do so by adding a static constructor (to avoid type-initialization optimizations) and a static method which is then called to force the initialization, it works fine:
public class DerivedStringEnum : StringEnum
{
// Other members as before.
static DerivedStringEnum()
{
}
public static void ForceInit()
{
}
}
class Test
{
static void Main()
{
string s = "EnumValue1";
DerivedStringEnum.ForceInit();
DerivedStringEnum e = (DerivedStringEnum) s;
Console.WriteLine(e); // EnumValue1
}
}
It's not something I'd recommend doing - I dislike it when the state of a base class effectively depends on whether some derived type has been initialized - but it does explain things...
Note that the answer by Andras works (or at least can work, although I don't think it's guaranteed to) because by invoking the operator declared in the derived type, you might end up initializing the type. You might not though - type initialization can be very lazy. I believe you'd have to actually use a field within the operator (before the call to the base conversion operator) to really force initialization.
With just the StringEnum
operator as per the original question, this line:
DerivedStringEnum e = (DerivedStringEnum) s;
is compiled to both the custom operator invocation and a cast:
IL_000d: ldloc.0
IL_000e: call class StringEnum StringEnum::op_Explicit(string)
IL_0013: castclass DerivedStringEnum
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With