在没有显式类型转换的情况下将 INT 值动态设置为 Nullable enum 属性

Dynamically set INT value to Nullable enum property without explicit type cast

我有一个方法可以将 DataTable 填充到简单的 DTO 对象。为了简化,我将使用这个例子:

public enum Gender : int
{
    Male = 1,
    Female = 2
}

public class Person
{
    //...
    public Gender? MyGender { get; set; }
}

static void Main(string[] args)
{
    int intValue = 2; // value from DB

    var o = new Person();
    var prop = o.GetType().GetProperty("MyGender");    
    prop.SetValue(o, intValue , null); // <- Exception
}

以上抛出:

Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[Test.Program+Gender]'.

如果我将 MyGender 声明为 Gender(不可为空),则一切正常。

如果我使用显式 Cast,它也有效 prop.SetValue(o, (Gender)intValue, null);

但是,我不想(也不能)使用显式转换:(Gender)intValue 因为我不了解底层 "hard" 创建 DTO 对象时键入。

我希望得到类似的东西(不能编译):

var propType = prop.PropertyType;
prop.SetValue(o, (propType)intValue, null);

我也试过:

public static dynamic Cast(dynamic obj, Type castTo)
{
    return Convert.ChangeType(obj, castTo);
}    
var propType = prop.PropertyType;
prop.SetValue(o, Cast(intValue, propType), null);

抛出:

Invalid cast from 'System.Int32' to 'System.Nullable`1[[Test.Program+Gender...]

我走投无路了。我有什么选择?

.NET Framework 4.6.2

这是我能想到的最好的。有一个显式检查以查看分配给的 属性 是否可为空,但我认为您无法避免这种情况。

public static void Main(string[] args)
{
    int intValue = 2; // value from DB

    var o = new Person();
    var prop = o.GetType().GetProperty("MyGender");   

    // Check whether the property is a nullable. If it is, get the type of underling enum
    // Otherwise, get the type of the enum directly from the property
    var enumType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
    // Convert the int to the enum type
    var convertedValue = Enum.ToObject(enumType, intValue);

    prop.SetValue(o, convertedValue , null);
}

当然,如果分配的 属性 不是枚举,就会发生不好的事情。如果您需要,var convertedValue = enumType.IsEnum ? Enum.ToObject(enumType, intValue); : intValue; 会避免这种情况。

要考虑的 "creative" 选项是:

var o = new Person();
o.MyGender = 0;
o.MyGender += intValue;

这看起来很奇怪,但它确实有效,因为 constant 0 有一个内置的 implicit cast 来枚举(其他数字没有)。

因此,您将其设置为 0,然后将其递增到您感兴趣的 实际 数字。这里的一个主要好处是您不会受到性能影响(和/或缺乏类型安全)使用反射。 您可能还想在代码中添加注释,说明您为什么这样做。 ;)