Использование отражения с родовыми типами и неявными преобразованиями - PullRequest
0 голосов
/ 20 июля 2009

Я пытаюсь использовать отражение, чтобы установить свойства для некоторых типов OpenXML (например, Обоснование). Присвоить значение путем перечисления всех возможностей просто:

// attr is an XmlAttribute, so .Name and .Value are Strings
if (attr.Name == "Val")
{
    if (element is Justification)
    {
        ((Justification)element).Val = (JustificationValues)Enum.Parse(typeof(JustificationValues), attr.Value);
            return;
    }
    else
    {
        // test for dozens of other types, such as TabStop
    }
}

Что делает это трудным сделать с помощью размышлений: 1) Тип свойства Val - EnumValue , поэтому я не знаю, как извлечь тип для передачи в качестве первого аргумента в Enum.Parse. 2) Существует неявное преобразование из фактического типа перечисления в тип EnumValue <>, который я не знаю, как вызывать с отражением.

Я бы хотел, чтобы код выглядел примерно так:

PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = ConvertToPropType(pInfo.PropertyType, attr.Value); /* this 
    would return an instance of EnumValue<JustificationValues> in this case */
pInfo.SetValue(element, value, null);

Как мне реализовать ConvertToPropType? Или есть лучшее решение?

Спасибо

Edit: Я получил решение, работающее с использованием предложения Earwicker, но оно опирается на удобный факт, что имя типа перечисления может быть получено из имени типа узла ("Justification" -> "JustificationValues"). Мне все еще интересно, как решить эту проблему в общем случае.

Edit2: GetGenericArguments дало мне остальную часть пути туда. Спасибо.

Ответы [ 3 ]

4 голосов
/ 20 июля 2009

Если значение атрибута - просто строка, я предполагаю, что у вас уже есть какой-то способ выяснить, что строка идентифицирует значение из определенного перечисления. В вашем примере это жестко запрограммировано, поэтому я не уверен, хотите ли вы этого или хотите изменить.

Предполагая, что вы знаете, что это перечисление, и знаете, какое перечисление, вы уже знаете, как получить объект, содержащий коробочное значение правильного типа enum, как в вашем фрагменте.

Теперь, если я предполагаю, что EnumValue<T> имеет конструктор, который принимает T.

Type genericType = typeof(EnumValue<>);
Type concreteType = genericType.MakeGenericType(typeof(JustificationValues));

Теперь concreteType - это тип EnumValue<JustificationValues>.

Отсюда вы можете получить конструктор, который, надо надеяться, принимает параметр JustificationValues и Invoke его.

Обновление

Ааа, я вижу, что вы делаете сейчас. Вы используете имя атрибута XML, чтобы выбрать свойство C #. Вы должны быть в состоянии определить, относится ли это свойство к типу EnumValue<T> и выяснить, что такое T.

PropertyInfo p = // ... get property info

Type t = p.GetType();

if (t.IsGenericType && 
    t.GetGenericTypeDefinition == typeof(EnumValue<>))
{
    Type e = t.GetGenericArguments()[0]; // get first (and only) type arg

    // e is the enum type...

Дайте этому попытку.

1 голос
/ 17 мая 2011

Это может работать только с базовыми типами, но этого было достаточно для того, что я делаю

PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = System.Convert.ChangeType(attr.Value, pInfo.PropertyType);
pInfo.SetValue(element, value, null);
1 голос
/ 18 апреля 2011

.Net 4.0 добавляет поддержку для преобразования с поздним привязанным или явным преобразованием. Это упрощено в фреймворке с открытым исходным кодом ImpromptuInterface с его статическим методом, называемым InvokeConvert . В вашем идеальном примере это будет работать так:

PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = Impromptu.InvokeConvert(attr.Value, pInfo.PropertyType); 
pInfo.SetValue(element, value, null);
...