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

时间:2012-03-26 16:00:40

标签: c# dictionary

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

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

StringEnum类定义如下:

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

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

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”)]。

谢谢!

2 个答案:

答案 0 :(得分:6)

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

由于运算符是静态的,因此它们不是继承的 - 显式强制转换运算符仅在stringStringEnum之间定义。你可以自己做这个丑陋的双重演绎:

DerivedStringEnum e = (DerivedStringEnum)(StringEnum)s

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

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

答案 1 :(得分:1)

需要添加其他运营商。你已经确定了真正的问题:

  

检查代码显示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的答案有效(或者至少可以工作,虽然我认为不能保证),因为通过调用派生类型中声明的运算符,您可以 最终初始化类型。你可能不会 - type initialization can be very lazy。我相信你必须在运算符中实际使用字段(在调用基本转换运算符之前)才能真正强制初始化。

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

DerivedStringEnum e = (DerivedStringEnum) s;

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

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