gpt4 book ai didi

c# - 递归映射 ExpandoObject

转载 作者:太空狗 更新时间:2023-10-29 20:24:17 27 4
gpt4 key购买 nike

在我的应用程序中,我必须使用 ExpandoObject 才能在运行时创建/删除属性;但是,我必须将函数返回的 ExpandoObject 映射到相应的对象/类。所以我想出了一个可以完成这项工作但有 3 个问题的小型 Mapper:

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

代码:

I- 实现:

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
}

二:用法:

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

最佳答案

3- If the property is formated like this public string Property; the get properties does not get it.

哦,那不是属性,那是字段。如果您还想考虑字段。

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

2- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.

为什么要检查 Nullable 类型,让反射来解决。如果值有效,它将被分配。

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 - It does not recursively map the inner objects of the ExpandoObject as supposed.

似乎至少适用于您的 InnerClass

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。您可以为字段编写类似的重载方法。

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

}
}

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

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

关于c# - 递归映射 ExpandoObject,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19529178/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com