【问题标题】:Generic class for enumerations - casting issue枚举的通用类 - 转换问题
【发布时间】:2015-09-15 16:17:42
【问题描述】:

我想编写一个接受枚举的泛型类。由于此类旨在实现某些接口,因此主要目的是能够将枚举视为实现这些接口的其他对象(例如,用于列表扩展等)。因此,对于一个示例枚举

public enum QEnum : int
{
    xlNoValue = 0,
    xlSomeValue = 1
}

public static class QEnumExtensions
{
    public static string toString(this QEnum xThis)
    {
        ...
    }

    public static QEnum toEnum(this string xThis)
    {
        ...
    }
}

我想声明一个泛型类,比如

public class QEnumHolder<T>  where T : struct, IConvertible
{
    private T mxVal = default(T);

    public QEnumHolder()
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
    }

    public QEnumHolder(T xVal) 
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
        mxVal = xVal;
    }

    static public implicit operator QEnumHolder<T>(T xVal)
    {
        return new QEnumHolder<T>(xVal);
    }

    static public implicit operator T(QEnumHolder<T> xVal)
    {
        return (T)xVal.mxVal;
    }

    public string toString()
    {
        if (mxVal is QEnum) return ((QEnum)Convert.ToInt32(mxVal)).toString();     
        ...
     }

    public void fromString(string xString)
    {
        if (mxVal is QEnum)
            mxVal = (???)xString.toEnum();       // problem
    }
}

我们使用的所有枚举都实现了

  • toString() 函数返回一个“漂亮”的字符串,可以进入组合框等
  • 字符串到枚举的转换,如上

因此 toString/toEnum 的结构几乎是给定的。问题在于标记为“问题”的最后一行代码。我不知道如何告诉编译器,在这个分支中,toEnum()T 的返回类型将是相同的。

我试图通过将mxVal 声明为int 并在任何地方使用Convert.ToInt32 来规避这个问题。但是,然后我在operator T 中遇到问题,其中编译器反对将int 转换为T(编译器不知道T 将是枚举,因此我不能使用任何一个关于 SO 的“int 到 enum 转换”讨论)。

【问题讨论】:

  • 作为一般规则 C# 使用驼峰式大小写,对于 ToString() 你应该覆盖。
  • @Bauss:我知道,我只是不喜欢它。 Equals() 等也是如此 - 如果你不覆盖它,你会得到一个你不关心的实现,并且没有人通知你。
  • 这个设计有很多问题。如果您的类包含 is QEnum(QEnum)something) 之类的代码,即 具体 类型,那么它不是 泛型类。此外,您不能有多个 ToEnum(string) 扩展方法,它们仅在返回类型上有所不同。
  • @Ivan:是的,它不是泛型的,但是鉴于泛型不能仅限于枚举,你有更好的主意吗?至于第二点,枚举QCountry会有toCountry(this string ..)
  • 缺少enum 约束是无关的。顺便说一句,您不必在每个构造函数中检查 - 只需创建一个 static 构造函数并将代码 if (!typeof(T).IsEnum)... 放在那里。问题是所有ifs 的需要。您是否真的打算为每个枚举编写所有这些扩展方法并在您的持有者类 toStringfromString 方法中添加相应的 if

标签: c# generics enums


【解决方案1】:

更好的设计是使用一些命名约定,将所有枚举扩展方法放在同一个静态类中,并将这些函数绑定到您的持有者类 static 构造函数中。像这样的:

public static partial class MyEnumExtensions
{
    public static MyEnumHolder<T> ToHolder<T>(this T source)
        where T : struct, IConvertible
    {
        return new MyEnumHolder<T>(source);
    }
}

public class MyEnumHolder<T> where T : struct, IConvertible
{
    static readonly Func<T, string> toStringFunc;
    static readonly Func<string, T> toEnumFunc;
    static MyEnumHolder()
    {
        if (!typeof(T).IsEnum) throw new NotSupportedException();
        // Use your naming conventions
        var name = typeof(T).Name;
        toStringFunc = (Func<T, string>)Delegate.CreateDelegate(typeof(Func<T, string>),
            typeof(MyEnumExtensions).GetMethod("toString", new[] { typeof(T) }));
        toEnumFunc = (Func<string, T>)Delegate.CreateDelegate(typeof(Func<string, T>),
            typeof(MyEnumExtensions).GetMethod("to" + name, new[] { typeof(string) }));
    }

    private T value;
    public MyEnumHolder() { value = default(T); }
    public MyEnumHolder(T value) { this.value = value; }
    static public implicit operator MyEnumHolder<T>(T x) { return new MyEnumHolder<T>(x); }
    static public implicit operator T(MyEnumHolder<T> x) { return x.value; }
    public string toString()
    {
        return toStringFunc(value);
    }
    public void fromString(string xString)
    {
        value = toEnumFunc(xString);
    }
}

示例枚举定义(可以在不同的文件中,但必须在同一个项目中):

public enum MyEnumA { A1, A2, A3 }
partial class MyEnumExtensions
{
    public static string toString(this MyEnumA x)
    {
        //...
        return x.ToString();
    }
    public static MyEnumA toMyEnumA(this string x)
    {
        //...
        return (MyEnumA)Enum.Parse(typeof(MyEnumA), x);
    }
}

public enum MyEnumB { B1, B2, B3 }
partial class MyEnumExtensions
{
    public static string toString(this MyEnumB x)
    {
        //...
        return x.ToString();
    }
    public static MyEnumB toMyEnumB(this string x)
    {
        //...
        return (MyEnumB)Enum.Parse(typeof(MyEnumB), x);
    }
}

测试:

var a = MyEnumA.A1.ToHolder();
var sA = a.toString();
a.fromString("A2");
var b = MyEnumB.B2.ToHolder();
var sB = b.toString();
b.fromString("B1");

【讨论】:

  • Ivan,非常感谢,你的代码比我的优雅多了。
【解决方案2】:
mxVal = (T)(object)xString.toEnum();

【讨论】:

  • 就像我说的......“如果解决方案是微不足道的”。似乎可行,我将等待一个小时进行潜在的讨论,然后接受。非常非常感谢。
  • 很高兴为您提供帮助!我之前在处理泛型时遇到过这个问题 - 花了一些时间来解决这个问题
猜你喜欢
  • 2013-01-10
  • 1970-01-01
  • 2011-10-02
  • 2023-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
相关资源
最近更新 更多