我的代码想要迭代包含类型的FieldInfo和PropertyInfo的Dictionary,并使用它来将值从一个对象映射到另一个对象。例如:
public static void MapFieldsAndProperties(object source, object target)
{
Dictionary<string, MemberInfo> target_properties = ClassUtils.GetPropertiesAndFields(target);
Dictionary<string, MemberInfo> source_properties = ClassUtils.GetMatchingPropertiesAndFields(target_properties.Keys, source);
foreach (var entry in source_properties)
{
var sourceProperty = entry.Value;
var targetProperty = target_properties[entry.Key];
// for now, match datatypes directly
if (dataTypesMatch(source, target))
{
var sourceValue = sourceProperty.GetValue(source);
try
{
targetProperty.SetValue(target, sourceValue);
}
catch (TargetException e)
{
LOG.ErrorFormat("unable to set value {0} for property={1}, ex={2}", sourceValue, targetProperty, e);
}
}
}
}
以上问题是:
1)dataTypesMatch()
函数需要2个不同的方法签名,一个用于FieldInfo
,一个用于PropertyInfo
(然后检查每个的类型并适当地转换以分派到正确的函数)。这是因为要检查字段数据类型使用FieldInfo.FieldType
,而属性的数据类型使用PropertyInfo.PropertyType
。
2)即使FieldInfo
和PropertyInfo
都有SetValue
和GetValue
方法,它们也不是从公共父类派生的,所以它再次需要强制转换。 (也许Dynamic会处理这个问题?)
是否有一个解决方案允许一般地处理这两种类型的MemberInfo对象来检查DataType和Get / SetValue?
答案 0 :(得分:1)
为什么不修改方法以获取两个Type类型的参数,并相应地传递FieldInfo.FieldType
和PropertyInfo.PropertyType
?
答案 1 :(得分:1)
我也遇到了这个问题,但是我不想使用接口,因为接口在紧密的循环中会带来严重的性能损失。由于我正在编写需要尽可能快的序列化程序,因此我使用了MemberInfo的扩展方法解决方案。
public static void SetValue(this MemberInfo mi, object targetObject, object value)
{
switch (mi.MemberType)
{
case MemberTypes.Field:
try
{
(mi as FieldInfo).SetValue(targetObject, value);
}
catch(Exception e)
{
throw new GeneralSerializationException($"Could not set field {mi.Name} on object of type {targetObject.GetType()}.", e);
}
break;
case MemberTypes.Property:
try
{
(mi as PropertyInfo).SetValue(targetObject, value);
}
catch(Exception e)
{
throw new GeneralSerializationException($"Could not set property {mi.Name} on object of type {targetObject.GetType()}.", e);
}
break;
default:
throw new GeneralSerializationException($"MemberInfo must be a subtype of FieldInfo or PropertyInfo.");
}
}
所以现在您可以调用MemberInfo.SetValue(object,value)。
您可以为其他成员和您需要访问的方法设置其他扩展方法。
更新8.31.2019 :我更新了代码,使其更加健壮,并提供了更有意义的错误报告。
答案 2 :(得分:0)
由于似乎没有任何本机解决方案,我将PropertyInfo
和FieldInfo
对象包装在一个接口中,该接口使客户端代码能够使用其相关属性和方法,而无需分支并将它们转换为代码的主体。
public interface IGetterSetter
{
Type DataType { get; }
string Name { get; }
MemberInfo UnderlyingMember { get; }
bool CanRead { get; }
bool CanWrite { get; }
object GetValue(object obj);
void SetValue(object obj, object value);
}
因此,将公共Field和Property值复制到目标对象的循环现在如下所示:
public static void Copy(object source, object target, ObjectMapperCopyValidator rules)
{
Dictionary<string, IGetterSetter> target_properties = ClassUtils.GetGetterSetters(target);
Dictionary<string, IGetterSetter> source_properties = ClassUtils.GetMatchingGetterSetters(target_properties.Keys, source);
foreach (var entry in source_properties)
{
var sourceProperty = entry.Value;
var targetProperty = target_properties[entry.Key];
// for now, match datatypes directly
if (sourceProperty.DataType == targetProperty.DataType)
{
// if property not readable or writeable, skip
if (!(sourceProperty.CanRead && targetProperty.CanWrite))
{
continue;
}
var sourceValue = sourceProperty.GetValue(source);
try
{
if (rules.IsValid(sourceProperty, sourceValue))
{
targetProperty.SetValue(target, sourceValue);
}
}
catch (TargetException e)
{
LOG.ErrorFormat("unable to set value {0} for property={1}, ex={2}", sourceValue, targetProperty, e);
}
}
}
}
将PropertyInfo和FieldInfo包装到公共接口中是最简单的部分:
public static Dictionary<string, IGetterSetter> GetGetterSetters(object target)
{
return target.GetType().GetMembers().Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).ToDictionary(x => x.Name, x => GetterSetterFactory.Instance.Create(x));
}
因此PropertyInfo
方法中隐藏了规范化和投射FieldInfo
和GetterSetterFactory.Instance.Create(x)
的丑陋。