【问题标题】:Map two objects using reflection c#使用反射c#映射两个对象
【发布时间】:2018-06-07 21:58:38
【问题描述】:

我正在创建一个函数来循环一个对象及其子对象。

但是当我尝试从原始对象映射到新对象时遇到了一些问题,代码如下:

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") 这样的父级字段时,它工作得很好

但是当我尝试创建子结构时,当我尝试映射到新对象时,它会中断。

我注意到属性destination 到方法MapValues&lt;T&gt; 被映射为Country.Address,所有属性都为null,这是正确的。 但是(typeof(T)).GetProperties() 不返回任何内容。 我还注意到 Activator.CreateInstance(typeof(T)) retunrs 输入 {object} 而不是 return Country.Address 这可能是无法正常工作的原因。

知道如何解决这个问题吗?

编辑:添加带有值的 get 属性-> 它返回 Dictionary&lt;string, object&gt;

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;
}

【问题讨论】:

  • GetPropertiesWithValues 是在哪里定义的?
  • 您为什么不只是使用 AutoMapper?
  • @EvdzhanMustafa 我添加了函数,但基本上是返回一个Dictionary&lt;string, object&gt;
  • @LasseVågsætherKarlsen 它没有安装在应用程序中,我只使用了几次,但老实说,当我第一次想到这个方法时,我并没有预料到这个错误。你认为自动映射器可以解决这个问题吗?
  • 据我了解,您想匹配特定属性结构中的特定属性值,对吗?那你为什么要在每个递归步骤中创建一个实例并做各种疯狂的事情,而不是递归搜索特定属性然后进行值匹配?

标签: c# reflection


【解决方案1】:

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的目的是什么:

  • 是创建一个实例并填充它,然后返回它吗?
  • 是否接受现有实例并填充它?

两者都足够简单,但目前您介于两者之间。另请注意,您只传递了一个单个 PropertyInfo - 考虑一下您希望如何分配多个属性。

最后,还有值来自的问题。您目前在PropertyInfo 上拨打GetPropertiesWithValues - 这不会像您期望的那样做。您需要提供源对象本身,否则无处可取值。

【讨论】:

【解决方案2】:

我在这篇文章中找到了我需要做的事情:Getting Nested Object Property Value Using 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;   
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    相关资源
    最近更新 更多