使用用户定义的转换将字符串转换为type-safe-enum

为了将Enum与字符串结合使用,我实现了一个基于https://stackoverflow.com/a/424414/1293385的StringEnum类。

但是,当我尝试实现建议的用户定义转换操作时,我遇到了问题。

StringEnum类定义如下:

public abstract class StringEnum { private readonly String name; private readonly int value; protected static Dictionary instances = new Dictionary(); 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; } } 

我使用这个类作为这样的基础:

 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) { } } 

但是,当我尝试使用它时

 string s = "EnumValue1" DerivedStringEnum e = (DerivedStringEnum) s; 

返回InvalidCastException。 检查代码显示StringEnum类的instances属性永远不会被填充。

有没有一种简单的方法来解决这个问题?

我不想使用C#属性“magic”,例如[StringValue(“EnumValue1”)]。

谢谢!

您还必须在派生类上定义显式转换运算符。 预计基类不知道如何转换为派生类。

由于运算符是静态的,因此它们不是inheritance的 – 显式转换运算符仅在stringStringEnum之间定义。 你可以自己做这个相当难看的双重演员:

 DerivedStringEnum e = (DerivedStringEnum)(StringEnum)s 

或者在你的派生类中你可以放:(在@ili指出我自己的疏忽后编辑)

 public static explicit operator DerivedStringEnum(string name) { return (DerivedStringEnum)(StringEnum)name; } 

您不需要添加其他运算符。 你已经确定了真正的问题:

检查代码显示StringEnum类的instances属性永远不会被填充。

那是因为没有任何东西强迫DerivedStringEnum类被初始化。 你永远不会指任何迫使它被初始化的东西。 如果你这样做是通过添加静态构造函数(以避免类型初始化优化)和静态方法,然后调用它来强制初始化,它可以正常工作:

 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 } } 

这不是我建议做的事情 – 当基类的状态实际上取决于某些派生类型是否已被初始化时我不喜欢它 – 但它确实解释了事情……

请注意Andras的答案是有效的(或者至少可以工作,虽然我认为不能保证),因为通过调用派生类型中声明的运算符,您可能最终初始化类型。 你可能不会 – 类型初始化可能非常懒惰 。 我相信你必须在运算符中实际使用一个字段 (在调用基本转换运算符之前)才能真正强制初始化。

根据原始问题使用StringEnum运算符,这一行:

 DerivedStringEnum e = (DerivedStringEnum) s; 

被编译为自定义操作符调用强制转换:

 IL_000d: ldloc.0 IL_000e: call class StringEnum StringEnum::op_Explicit(string) IL_0013: castclass DerivedStringEnum