【问题标题】:Cast Int to Generic Nullable Enum in C#将 Int 转换为 C# 中的通用可空枚举
【发布时间】:2014-08-14 02:03:49
【问题描述】:

我想将一个 int 值转换为使用通用函数的可空枚举。我认为这很容易,尤其是所有关于 enum / int 转换的 SO 。我发现的最接近的问题是this one,但不幸的是,它不处理可为空的枚举。 This question 解决 Nullable 枚举转换问题,但不是使用泛型。

这是我正在尝试做的一个简化示例:

public enum SouthParkCharacters
{
    Stan = 0,
    Kyle = 1,
    Eric = 2,
    Kenny = 3
}

public static T CastNullableEnum<T>(int value)
{
    return (T)(object)((int?)value).Value;
}


public static T SomeFunction<T>(this int anyEnumValue)
{
        SouthParkCharacters? spc = SouthParkCharacters.Kenny;

        spc = CastNullableEnum<SouthParkCharacters>(3); //I am working!
        spc = CastNullableEnum<SouthParkCharacters?>(3); //Raise error Specified cast is not valid.

        //This function will be public so I won't have control over the T
        T finalValue = CastNullableEnum<T>(anyEnumValue); //Raise error Specified cast is not valid.

        return finalValue;

}

正如您在最后一个函数中看到的,一个不可为空的枚举的强制转换正在工作。问题是我希望能够从其他泛型函数调用 CastNullableEnum,它可以使用可为空的枚举作为泛型类型。

我知道您可以将泛型类型作为可空泛型 CastNullableEnum&lt;T?&gt; 传递,但我们可以做相反的事情(即将可空基础类型传递给泛型函数)吗?

如果无法做到这一点,那么我想解决方案是在 CastNullableEnum 函数中找到正确的方法来转换枚举值。

【问题讨论】:

  • 我认为 C# 对泛型枚举的支持有点弱。我真的希望你能说“where T: enum”,但“where T: struct”是你能得到的最接近的。也就是说,为什么您希望能够让相同的方法同时返回裸枚举和 Nullable?让你的函数总是返回 Nullable 并强制调用者检查结果是否有效似乎更好。如果枚举实际上没有具有整数值的成员,我假设您想返回 null 。或者我错过了什么?
  • 南方公园角色?不是结构,所以这里 T:struct 不适用。
  • 长话短说,此函数将用于将数据从 dbDataReader 映射到给定对象的属性。由于我无法控制给定的对象或通用类型,我想处理我身边的每个铸造问题。
  • @The_Black_Smurf 您是否考虑过将枚举值之一定义为 None,即 None = 0, 而不是尝试使用 null?同样值得考虑的是将您的枚举值作为字符串 (nvarchar) 存储在数据库中。它在查询或报告时增加了可读性,并且可以使用 ToString 和 Enum.TryParse 方法轻松处理。
  • @JamieSee,是的,我确实考虑在枚举中使用 none 值来满足我的需要。问题是我的库可能被用作第三方,因此我将无法控制提供给我的函数的通用类型(也无法控制枚举结构/它将存储在数据库中的方式)。

标签: c# generics casting enums nullable


【解决方案1】:

我可能读错了问题,购买你可以从可为空的对象中获取基础类型并使用 Enum.Parse 而不是直接强制转换。

public T CastNullableEnum<T>(int value) 
{
    var enumType = Nullable.GetUnderlyingType(typeof(T));
    if (Enum.IsDefined(enumType, value)) 
    {
        return (T)(object)Enum.Parse(enumType, value.ToString());
    }
    return default(T);
}

var test1 = CastNullableEnum<SouthParkCharacters?>(3); 
//returns Kenny

var test2 = CastNullableEnum<SouthParkCharacters?>(9); 
// returns null

【讨论】:

    【解决方案2】:

    这将起作用:

    public static T IntToEnum<T>(object value)
    {
        if (value == null)
            return (T)value;
    
        else if (typeof(T).IsEnum)
            return (T)value;
    
        else if (Nullable.GetUnderlyingType(typeof(T))?.IsEnum == true)
            return (T)Enum.ToObject(Nullable.GetUnderlyingType(typeof(T)), (int)value);
    
        throw new Exception(string.Format("Value cannot be converted from {0} to {1}", value.GetType().Name, typeof(T).Name));
    }
    

    可用于可空和常规枚举

    public enum Codes
    {
        A = 1,
        B = 2,
        C = 3
    }
    
    static void Main(string[] args)
    {
        Console.WriteLine(IntToEnum<Codes?>(1));
        Console.WriteLine(IntToEnum<Codes?>(null));
        Console.WriteLine(IntToEnum<Codes>(2));
    }
    

    【讨论】:

      【解决方案3】:

      有趣的问题。这段代码的问题是将整数转换为可空枚举,这是不可能的。

        SouthParkCharacters? enumValue = (SouthParkCharacters?)int.MaxValue;
        if (enumValue.HasValue)
           {
               Console.WriteLine("it still has value");
           }
      

      上面的代码正在运行,结果 enumValue 仍然是 HasValue。

      但是无论如何,如果你看一下编译后的C#代码,它会是这样的:

      public static T CastNullableEnum<T>(int value)
          {
              Type type = typeof(T);
              return (T)((object)value);
          }
          public static T SomeFunction<T>(int anyEnumValue)
          {
              Program.SouthParkCharacters? spc = new Program.SouthParkCharacters?(Program.SouthParkCharacters.Kenny);
              spc = new Program.SouthParkCharacters?(Program.CastNullableEnum<Program.SouthParkCharacters>(new int?(3)));
              spc = Program.CastNullableEnum<Program.SouthParkCharacters?>(new int?(3));
              return Program.CastNullableEnum<T>(new int?(anyEnumValue));
          }
      

      现在,注意

      spc = new Program.SouthParkCharacters?(Program.CastNullableEnum<Program.SouthParkCharacters>(new int?(3))); 
      

      这就是 Unspecified Cast Exception 的来源;因为新的 SouthParkCharacters?(int value) 需要一个 Integer。

      有趣的是,当你使用即时运行时,你不会遇到这个问题。有意思吧?

      【讨论】:

      • 是的,我注意到即时窗口的不同之处......我花了 15 分钟来了解我的测试发生了什么。这真是一种奇怪的行为。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-09
      • 2013-06-02
      • 1970-01-01
      • 1970-01-01
      • 2012-07-12
      • 1970-01-01
      相关资源
      最近更新 更多