Динамически установить значение INT в Nullable enum свойство без явного приведения типа

У меня есть метод, который заполняет 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
}

Вышеуказанные броски:

Объект типа 'System.Int32' нельзя преобразовать в тип 'System.Nullable`1[Test.Program+Gender]'.

Если я заявляю MyGender как Gender (не Nullable) все работает отлично.

Это также работает, если я использую явное приведение prop.SetValue(o, (Gender)intValue, null);

НО, я не хочу (и не могу) использовать явное приведение: (Gender)intValue потому что я не знаю базового "жесткого" типа при создании объекта 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);

Который бросает:

Неверное приведение из 'System.Int32' к 'System.Nullable`1[[Test.Program+Gender...]

Я в тупике. какие у меня варианты?

.NET Framework 4.6.2

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; бы избежать этого, если бы вам это было нужно.

"Творческий" вариант для рассмотрения:

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

Это выглядит странно, но это работает, так как константа 0 имеет встроенное неявное приведение к enum (чего нет у других чисел).

Таким образом, вы устанавливаете его на 0, а затем увеличиваете его до фактического числа, которое вас интересует. Ключевое преимущество здесь заключается в том, что вы не берете удар по производительности (и / или отсутствие безопасности типов) при использовании отражения. Возможно, вы захотите добавить комментарий к коду относительно того, почему вы это делаете. ;)

Другие вопросы по тегам