首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归映射ExpandoObject

递归映射ExpandoObject
EN

Stack Overflow用户
提问于 2013-10-22 21:59:16
回答 1查看 3.3K关注 0票数 7

在我的应用程序中,为了在运行时创建/删除属性,我必须使用ExpandoObject;但是,我必须将函数的返回ExpandoObject映射到相应的对象/类。所以我想出了一个小地图来做这个工作,但是有三个问题:

  1. 它不像假定的那样递归映射ExpandoObject的内部对象。
  2. 当我试图简单地将int映射到一个Nullable时,它会抛出一个类型错配,因为我找不到正确检测和转换它的方法。
  3. 字段不能映射为public string Property;

代码:

一、执行情况:

代码语言:javascript
复制
public static class Mapper<T> where T : class
{
    #region Properties

    private static readonly Dictionary<string, PropertyInfo> PropertyMap;

    #endregion

    #region Ctor

    static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p => p.Name.ToLower(), p => p); }

    #endregion

    #region Methods

    public static void Map(ExpandoObject source, T destination)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (destination == null)
            throw new ArgumentNullException("destination");

        foreach (var kv in source)
        {
            PropertyInfo p;
            if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
            {
                Type propType = p.PropertyType;
                if (kv.Value == null)
                {
                    if (!propType.IsByRef && propType.Name != "Nullable`1")
                    {
                        throw new ArgumentException("not nullable");
                    }
                }
                else if (kv.Value.GetType() != propType)
                {
                    throw new ArgumentException("type mismatch");
                }
                p.SetValue(destination, kv.Value, null);
            }
        }
    }

    #endregion
}

二:用法:

代码语言:javascript
复制
public static void Main()
{
    Class c = new Class();
    dynamic o = new ExpandoObject();
    o.Name = "Carl";
    o.Level = 7;
    o.Inner = new InnerClass
              {
                      Name = "Inner Carl",
                      Level = 10
              };

    Mapper<Class>.Map(o, c);

    Console.Read();
}

internal class Class
{
    public string Name { get; set; }
    public int? Level { get; set; }
    public InnerClass Inner { get; set; }
    public string Property;
}

internal class InnerClass
{
    public string Name { get; set; }
    public int? Level { get; set; }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-10-22 23:27:34

3-如果属性是像这个public string Property;那样格式化的,那么get属性就不会得到它。

哦,那不是财产,那是一块田地。如果您也想考虑字段的话。

代码语言:javascript
复制
static Mapper()
{
    PropertyMap = typeof(T).GetProperties(BindingFlags.Public |
                                              BindingFlags.NonPublic |
                                              BindingFlags.Instance)
                                              .ToDictionary(p => p.Name.ToLower(), p => p);

    FieldMap = typeof(T).GetFields(BindingFlags.Public |
                                                BindingFlags.NonPublic |
                                                BindingFlags.Instance)
                                                .ToDictionary(f => f.Name.ToLower(), f => f);
}

当我试图简单地将int映射到一个Nullable时,它会抛出一个类型错配,因为我找不到正确检测和转换它的方法。

为什么检查Nullable类型,让反射出来。如果值是有效的,它将被赋值。

代码语言:javascript
复制
public static void Map(ExpandoObject source, T destination)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (destination == null)
        throw new ArgumentNullException("destination");

    foreach (var kv in source)
    {
        PropertyInfo p;
        if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
        {
            p.SetValue(destination, kv.Value, null);
        }
        else
        {
            FieldInfo f;
            if (FieldMap.TryGetValue(kv.Key.ToLower(), out f))
            {
                f.SetValue(destination, kv.Value);
            }
        }
    }
}

1-它不像假定的那样递归映射ExpandoObject的内部对象。

似乎至少对你的InnerClass是有效的。

代码语言:javascript
复制
Class c = new Class();
dynamic o = new ExpandoObject();
o.Name = "Carl";
o.Level = 7;
o.Inner = new InnerClass
{
    Name = "Inner Carl",
    Level = 10
};

o.Property = "my Property value"; // dont forget to set this

Mapper<Class>.Map(o, c);

编辑:基于您的评论,我创建了两个重载方法MergeProperty。您可以为字段编写类似的重载方法。

代码语言:javascript
复制
public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target)
{
    Type propType = pi.PropertyType;

    // dont recurse for value type, Nullable<T> and strings
    if (propType.IsValueType || propType == typeof(string))
    {
        var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value;
        if(sourceVal != null)
            pi.SetValue(target, sourceVal, null);
    }
    else // recursively map inner class properties
    {
        var props = propType.GetProperties(BindingFlags.Public |
                                                  BindingFlags.NonPublic |
                                                  BindingFlags.Instance);

        foreach (var p in props)
        {
            var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value;
            var targetPropValue = pi.GetValue(target, null);

            if (sourcePropValue != null)
            {
                if (targetPropValue == null) // replace
                {
                    pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null);
                }
                else
                {
                    MergeProperty(p, sourcePropValue, targetPropValue);
                }
            }
        }

    }
}

public static void MergeProperty(PropertyInfo pi, object source, object target)
{
    Type propType = pi.PropertyType;
    PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name);

    // dont recurse for value type, Nullable<T> and strings
    if (propType.IsValueType || propType == typeof(string)) 
    {
        var sourceVal = sourcePi.GetValue(source, null);
        if(sourceVal != null)
            pi.SetValue(target, sourceVal, null);
    }
    else // recursively map inner class properties
    {
        var props = propType.GetProperties(BindingFlags.Public |
                                                  BindingFlags.NonPublic |
                                                  BindingFlags.Instance);

        foreach (var p in props)
        {
            var sourcePropValue = sourcePi.GetValue(source, null);
            var targetPropValue = pi.GetValue(target, null);

            if (sourcePropValue != null)
            {
                if (targetPropValue == null) // replace
                {
                    pi.SetValue(target, sourcePi.GetValue(source, null), null);
                }
                else
                {
                    MergeProperty(p, sourcePropValue, targetPropValue);
                }
            }
        }

    }
}

您可以这样使用这些方法:

代码语言:javascript
复制
public static void Map(ExpandoObject source, T destination)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (destination == null)
        throw new ArgumentNullException("destination");

    foreach (var kv in source)
    {
        PropertyInfo p;
        if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p))
        {
            MergeProperty(p, source, destination);
        }
        else
        {
            // do similar merge for fields
        }
    }
}
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19529178

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档