Отобразить два объекта с помощью отражения c # - PullRequest
0 голосов
/ 07 июня 2018

Я создаю функцию для зацикливания объекта и его дочерних элементов.

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

public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{

    try
    {
        var objectroutelist = objectRoute.Split('.');
        var objroute = objectroutelist.First();

        var childproperty = typeof(T).GetProperty(objroute);

        if (objectroutelist.Count() == 1)
        {
            if (childproperty.GetValue(obj).ToString().Trim() == value)
            {
                return true;
            }
            return false;
        }
        else
        {
            var instance = Activator.CreateInstance(childproperty.PropertyType);
            //childproperty.SetValue(obj, instance, null);
            //childproperty.SetValue(instance, obj, null);

            instance.MapValues(childproperty);

            instance.MatchObjectField(string.Join(".", objectroutelist.Skip(1)), value);
        }

    }
    catch (Exception e)
    {

        return false;
    }
    return false;
}

А вот класс, где я делаю карту и содержит проблему.

public static void MapValues<T>(this T destination, PropertyInfo orgproperty)
{


    var orgvalues = orgproperty.GetPropertiesWithValues();

    var instance = Activator.CreateInstance(typeof(T));
    foreach (var property in (typeof(T)).GetProperties())
    {
        try
        {
            var value = orgvalues.FirstOrDefault(a => a.Key == property.Name);
            property.SetValue(instance, value);
        }
        catch (Exception)
        {
            property.SetValue(instance, null);
        }
    }
    destination = (T)(object)instance;
}

Идея функции заключается в вызове с objectName.MatchObjectField("parent.child.child.child","MyName")

КогдаЯ пытаюсь сравнить поле в родительском элементе, например objectName.MatchObjectField("Country","Ireland"), оно отлично работает

Но когда я пытаюсь создать дочернюю структуру, делая вызов, подобный objectName.MatchObjectField("Country.Address.City","Dublin"), оно разрывается, когда я пытаюсь отобразить новый объект.

Я заметил, что свойство destination в методе MapValues<T> отображается как Country.Address со всеми свойствами как null, что является правильным.Но (typeof(T)).GetProperties() ничего не возвращает.Также я заметил, что Activator.CreateInstance(typeof(T)) retunrs типа {object} вместо return Country.Address, что может быть причиной, почему не работает.

Есть идеи, как это исправить?

РЕДАКТИРОВАТЬ: добавить свойства get со значениями-> это повторяет Dictionary<string, object>

public static Dictionary<string, object> GetPropertiesWithValues(this Object obj, bool includeValueTypes = true)
{
    return InternalGetProperties(obj, false, includeValueTypes);
}

private static Dictionary<string, object> InternalGetProperties(Object obj, bool withDefaultValue, bool includeValueTypes = true)
{
    Dictionary<string, object> d = new Dictionary<string, object>();
    var res = GetPropertiesR(obj, d, "", withDefaultValue, includeValueTypes);
    return res;
}
private static Dictionary<string, object> GetPropertiesR(Object obj, Dictionary<string, object> d, string parent, bool searchingForDefaults, bool includeValueTypes)
{
    if (obj == null)
        return d;


    var pis = @obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
    if (pis == null || pis.Length == 0)
        throw new InvalidOperationException("This object doens't have inner properties");

    Func<string, string> formatProperty = (property) => string.Concat(parent, parent == "" ? "" : ".", property);

    foreach (var pi in pis)
    {
        object data = pi.GetValue(@obj, null);

        // check if is value type

        if (pi.PropertyType.IsValueType)
        {
            // data is never null

            if (!includeValueTypes)
                continue;

            Type nullableType = Nullable.GetUnderlyingType(pi.PropertyType);
            object defaultValue = nullableType != null ? GetDefault(nullableType) : GetDefault(data.GetType());


            if (!searchingForDefaults)
            {
                // check default values.
                if (data != null && data.ToString() != defaultValue.ToString())
                    d.Add(formatProperty(pi.Name), data);
            }
            else
            {
                // check default values
                if ((nullableType != null && data == null) || data.ToString() == defaultValue.ToString())
                    d.Add(formatProperty(pi.Name), data);
            }
        }
        else
        {
            //
            // reference types

            if (!searchingForDefaults)
            {
                if (data == default(object))
                    continue;

                if (IsThisPropertyPartofSystemNamespace(pi))
                {
                    // transform list into a string with values.
                    IEnumerable enumeration = data as IList;

                    if (enumeration != null)
                    {
                        StringBuilder sb = new StringBuilder();
                        foreach (var i in enumeration)
                            sb.Append(string.Concat(i.ToString(), ", "));

                        if (sb.Length >= 2)
                            sb.Remove(sb.Length - 2, 2);

                        data = sb.ToString();
                    }

                    d.Add(formatProperty(pi.Name), data);
                }

                else
                {

                    //
                    // user complex type defined
                    string ctxParent = string.Concat(parent, parent == "" ? "" : ".", pi.Name);
                    GetPropertiesR(data, d, ctxParent, searchingForDefaults, includeValueTypes);
                }
            }
            else
            {
                if (data != default(object))
                    continue;

                d.Add(formatProperty(pi.Name), data);
            }
        }

    }

    return d;
}

private static bool IsThisPropertyPartofSystemNamespace(PropertyInfo pi)
{
    var systemNames = new HashSet<string>
                    {
                        "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}",
                        "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken={TOKENKEY}"
                    };

    var isSystemType = systemNames.Contains(pi.PropertyType.Assembly.FullName);
    return isSystemType;
}

private static object GetDefault(Type type)
{
    if (type.IsValueType)
    {
        return Activator.CreateInstance(type);
    }

    return null;
}

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Я нашел, что мне нужно сделать в этом посте: Получение значения свойства вложенного объекта с помощью Reflection Получение вложенного объекта - это простой способ того, что я притворялся.

public static object GetPropertyValue(object src, string propName)
{
    if (src == null) throw new ArgumentException("Value cannot be null.", "src");
    if (propName == null) throw new ArgumentException("Value cannot be null.", "propName");

    if(propName.Contains("."))//complex type nested
    {
        var temp = propName.Split(new char[] { '.' }, 2);
        return GetPropertyValue(GetPropertyValue(src, temp[0]), temp[1]);
    }
    else
    {
        var prop = src.GetType().GetProperty(propName);
        return prop != null ? prop.GetValue(src, null) : null;
    }
}

public static bool MatchObjectField<T>(this T obj, string objectRoute, string value)
{

    try
    {
        var propvalue = GetPropertyValue(obj, objectRoute);

        return ( propvalue.ToString().Trim() == value.Trim());
    }
    catch (Exception) {
       throw;   
    }
}
0 голосов
/ 07 июня 2018

TL; DR: сделать шаг назад.Опишите ответственность каждого из методов в отдельности и напишите для них тесты таким образом.Начните с метода «самого низкого уровня» и продолжайте свой путь.Это поможет вам понять, что происходит не так.


Здесь много проблем.Сначала посмотрите на эти две строки кода:

var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues(childproperty);

Тип возврата Activator.CreateInstance равен object, так что это эффективно:

var instance = Activator.CreateInstance(childproperty.PropertyType);
instance.MapValues<object>(childproperty);

Это не то, что вы хотите -вы хотите использовать childproperty.PropertyType в качестве аргумента типа для MapValues.Вы не знаете об этом во время компиляции, поэтому он не подходит как параметр типа.

Но кроме этого, у вашего MapValues метода есть большая проблема: он в основном игнорирует свой параметр.Единственный раз, когда он использует destination, находится в последней строке, когда ему присваивается новое значение:

destination = (T)(object)instance;

Этот параметр является параметром значения, поэтому его назначение в конце метода бессмысленно.

Вы должны решить, какова цель MapValues:

  • Это создать экземпляр и заполнить его, а затем вернуть?
  • Этопринять существующий экземпляр и заполнить его?

Либо достаточно просто реализовать, но в настоящий момент вы на полпути между ними.Также обратите внимание, что вы передаете только single PropertyInfo - подумайте, как вы можете назначить несколько свойств с этим.

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

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