Присвоить свойству типа используя отражение - PullRequest
0 голосов
/ 15 июня 2019

Я пытаюсь создать библиотеку, которая будет извлекать данные из веб-службы и отображать полученные значения в любой класс, предоставленный пользователем, с использованием атрибутов для украшения свойств целевого класса. Это прекрасно работает для базовых типов, однако некоторые «типы» являются пользовательским типом (ConvertableDecimal, ... Int, ... Float) из другой библиотеки, которая выполняет преобразования единиц. Числовые значения хранятся в общем свойстве типа «BaseValue».

Вот пример того, как будет выглядеть свойство класса, реализующее свойства этих типов:

[OEDProperty("Discharge Capacity Rated", "BaseValue")]
public ConvertableDecimal DischargeCapacity { get; set; } = new ConvertableDecimal(MeasureType.FlowRate);

"OEDProperty" - это класс атрибутов, который я создал для украшения свойств, и он принимает два входа:

  1. Отображаемое имя поля xml (например, «Номинальная емкость разряда») и
  2. Необязательный параметр с именем "TargetMember", в данном случае "BaseValue" ...

Вот метод отображения:

public static T Map<T> (OEDData OED, out string Errors)
{
    string mappingErrors = string.Empty;

    object retObj = Activator.CreateInstance (typeof (T)); //we'll reset this later if we need to, e.g. targeting a member
    PropertyInfo[] properties = retObj.GetType ().GetProperties();

    foreach (PropertyInfo pi in properties) 
    {
        OEDPropertyAttribute propAtt = (OEDPropertyAttribute) pi.GetCustomAttribute (typeof (OEDPropertyAttribute));
        if (propAtt != null) 
        {
            PropertyInfo piTargetMember = null;

            if (!string.IsNullOrEmpty (propAtt.TargetMember)) 
            {
                try 
                { 
                    piTargetMember = pi.PropertyType.GetProperty (propAtt.TargetMember); 
                }
                catch (Exception ex) 
                { 
                    mappingErrors += string.Format("Error locating target member \"{0}\" for type \"{1}\" when setting field \"{2}\".\r\nMake sure the target member name is spelled correctly. Target member names ARE case sensitive.\r\nError: {3}", 
                                                    propAtt.TargetMember, 
                                                    propAtt.GetType().Name, 
                                                    propAtt.Field.ToLower(), 
                                                    ex.Message); 
                }
            }

            if (propAtt.IsHeaderField) //header fields
            {
                /*snip*/
            } 
            else //fields
            {
                try 
                {
                    var fVal = OED.Fields.FirstOrDefault (f => f.FieldName.ToLower () == propAtt.Field.ToLower ()).Value;
                    var convertedFVal = (piTargetMember == null) ? ChangeType (fVal, pi.PropertyType) : ChangeType (fVal, piTargetMember.PropertyType);

                    if (piTargetMember == null) 
                    { 
                        pi.SetValue(retObj, convertedFVal); 
                    } 
                    else
                    {
                        pi.SetValue(retObj.GetType().GetProperty(propAtt.TargetMember), convertedFVal);
                        //error happens here
                        //error text: Non-static method requires a target
                    }
                }
                catch (Exception ex) 
                { 
                    mappingErrors += string.Format("Unable to map oed field value: \"{0}\".\r\nError: {1}", propAtt.Field.ToLower (), ex.Message); 
                }
            }
        }
    }

    Errors = mappingErrors;

    return (T) retObj;
}

Текст ошибки при попытке установить значение свойства: «Нестатическому методу требуется цель»

Я понял из этого поста ( Нестатическому методу требуется цель ), что это происходит из-за нулевой ссылки во время выполнения.

У меня вопрос, какие у меня есть варианты, чтобы эта библиотека работала и была гибкой с любыми пользовательскими типами, которые могут возникнуть в будущем.

Любые идеи будут очень признательны.

1 Ответ

0 голосов
/ 17 июня 2019

Для рассматриваемой строки, с ошибкой, я бы предложил следующие изменения:

piTargetMember.SetValue(pi.GetValue(retObj), ChangeType(fVal, piTargetMember.PropertyType));

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

Оставляет целевой объект, который вы хотите изменить, как свойство pi retObj, созданного ранее для этой конкретной цели.

А затем я переместил троичную функцию преобразования сверху в эту строку в качестве значения, потому что нет смысла спрашивать, дважды ли piTargetMember == null.


Edit:

В случае, если ваш конструктор для T не создает экземпляр свойства, вам, возможно, придется сделать это и в своем коде, который изменит эту строку на три строки:

object propInstance = Activator.CreateInstance(pi.PropertyType);
piTargetMember.SetValue(propInstance, ChangeType(fVal, piTargetMember.PropertyType));
pi.SetValue(retObj, propInstance);

Полный код

При анализе вашего кода я значительно упростил форматирование. Поэтому я выложу весь код в качестве примера:

public static T Map<T>(OEDData OED, out string Errors)
{
    string mappingErrors = string.Empty;

    object retObj = Activator.CreateInstance(typeof(T)); //we'll reset this later if we need to, e.g. targeting a member

    foreach (PropertyInfo pi in typeof(T).GetProperties()) 
    {
        if (pi.GetCustomAttribute(typeof(OEDPropertyAttribute)) is OEDPropertyAttribute propAtt) 
        {
            PropertyInfo piTargetMember = null;

            if (!string.IsNullOrEmpty(propAtt.TargetMember)) 
            {
                try 
                { 
                    piTargetMember = pi.PropertyType.GetProperty(propAtt.TargetMember); 
                }
                catch (Exception ex) 
                { 
                    mappingErrors += $"Error locating target member \"{propAtt.TargetMember}\" for type \"{propAtt.GetType().Name}\" when setting field \"{propAtt.Field.ToLower()}\"." + 
                                     $"\r\nMake sure the target member name is spelled correctly. Target member names ARE case sensitive.\r\nError: {ex.Message}\r\n"; 
                }
            }

            if (propAtt.IsHeaderField) //header fields
            {
                /*snip*/
            } 
            else //fields
            {
                try 
                {
                    var fVal = OED.Fields.FirstOrDefault((f) => string.Equals(f.FieldName, propAtt.Field, StringComparison.CurrentCultureIgnoreCase))?.Value;

                    if (piTargetMember == null)
                    {
                        pi.SetValue(retObj, ChangeType(fVal, pi.PropertyType));
                    }
                    else
                    {
                        object propInstance = pi.GetValue(retObj);
                        if (propInstance == null)
                        {
                            // construct the value which is the property pointed to by 'pi'
                            propInstance = Activator.CreateInstance(pi.PropertyType);
                            pi.SetValue(retObj, propInstance);
                        }
                        piTargetMember.SetValue(propInstance, ChangeType(fVal, piTargetMember.PropertyType));
                    }
                }
                catch (Exception ex) 
                { 
                    mappingErrors += $"Unable to map oed field value: \"{propAtt.Field.ToLower()}\".\r\nError: {ex.Message}\r\n";
                }
            }
        }
    }

    Errors = mappingErrors;

    return (T) retObj;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...