我需要按字段名映射ObjectV1和ObjectV2之间的所有字段值和子集合。ObjectV2与ObjectV1位于不同的命名空间中。
由于这两个类需要独立发展,因此排除了模板ClassV1和ClassV2之间的继承关系。我考虑使用反射(速度较慢)和二进制序列化(也很慢)来执行公共属性的映射。
有没有更好的方法?还有其他选择吗?
我需要按字段名映射ObjectV1和ObjectV2之间的所有字段值和子集合。ObjectV2与ObjectV1位于不同的命名空间中。
由于这两个类需要独立发展,因此排除了模板ClassV1和ClassV2之间的继承关系。我考虑使用反射(速度较慢)和二进制序列化(也很慢)来执行公共属性的映射。
有没有更好的方法?还有其他选择吗?
这是哪个版本的.NET?
浅拷贝:
在3.5中,您可以预编译一个Expression
来完成此操作。在2.0中,您可以轻松使用HyperDescriptor
来完成相同的操作。两者都比反射大大提高了性能。
在MiscUtil
中有一个预先制作的Expression
方法 - PropertyCopy
,可以直接使用:
DestType clone = PropertyCopy<DestType>.CopyFrom(original);
(浅复制结束)
在这种情况下,BinaryFormatter(在问题中提到)不是一个选项 - 它根本不起作用,因为原始类型和目标类型不同。如果数据是基于合同的,则 XmlSerializer 或 DataContractSerializer 将工作如果所有合同名称匹配,但如果可能,上面两个(浅层)选项会更快。
此外 - 如果您的类型带有常见序列化属性(XmlType
或 DataContract
),那么protobuf-net可以(在某些情况下)为您执行深度复制/更改类型:
DestType clone = Serializer.ChangeType<OriginalType, DestType>(original);
但这取决于类型具有非常相似的模式(实际上,它不使用名称,而是在属性上使用显式的“Order”等)
以下是我制作的解决方案:
/// <summary>
/// Copies the data of one object to another. The target object gets properties of the first.
/// Any matching properties (by name) are written to the target.
/// </summary>
/// <param name="source">The source object to copy from</param>
/// <param name="target">The target object to copy to</param>
public static void CopyObjectData(object source, object target)
{
CopyObjectData(source, target, String.Empty, BindingFlags.Public | BindingFlags.Instance);
}
/// <summary>
/// Copies the data of one object to another. The target object gets properties of the first.
/// Any matching properties (by name) are written to the target.
/// </summary>
/// <param name="source">The source object to copy from</param>
/// <param name="target">The target object to copy to</param>
/// <param name="excludedProperties">A comma delimited list of properties that should not be copied</param>
/// <param name="memberAccess">Reflection binding access</param>
public static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess)
{
string[] excluded = null;
if (!string.IsNullOrEmpty(excludedProperties))
{
excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
MemberInfo[] miT = target.GetType().GetMembers(memberAccess);
foreach (MemberInfo Field in miT)
{
string name = Field.Name;
// Skip over excluded properties
if (string.IsNullOrEmpty(excludedProperties) == false
&& excluded.Contains(name))
{
continue;
}
if (Field.MemberType == MemberTypes.Field)
{
FieldInfo sourcefield = source.GetType().GetField(name);
if (sourcefield == null) { continue; }
object SourceValue = sourcefield.GetValue(source);
((FieldInfo)Field).SetValue(target, SourceValue);
}
else if (Field.MemberType == MemberTypes.Property)
{
PropertyInfo piTarget = Field as PropertyInfo;
PropertyInfo sourceField = source.GetType().GetProperty(name, memberAccess);
if (sourceField == null) { continue; }
if (piTarget.CanWrite && sourceField.CanRead)
{
object targetValue = piTarget.GetValue(target, null);
object sourceValue = sourceField.GetValue(source, null);
if (sourceValue == null) { continue; }
if (sourceField.PropertyType.IsArray
&& piTarget.PropertyType.IsArray
&& sourceValue != null )
{
CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue);
}
else
{
CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue);
}
}
}
}
}
private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue)
{
//instantiate target if needed
if (targetValue == null
&& piTarget.PropertyType.IsValueType == false
&& piTarget.PropertyType != typeof(string))
{
if (piTarget.PropertyType.IsArray)
{
targetValue = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
}
else
{
targetValue = Activator.CreateInstance(piTarget.PropertyType);
}
}
if (piTarget.PropertyType.IsValueType == false
&& piTarget.PropertyType != typeof(string))
{
CopyObjectData(sourceValue, targetValue, "", memberAccess);
piTarget.SetValue(target, targetValue, null);
}
else
{
if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
{
object tempSourceValue = sourceField.GetValue(source, null);
piTarget.SetValue(target, tempSourceValue, null);
}
else
{
CopyObjectData(piTarget, target, "", memberAccess);
}
}
}
private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue)
{
int sourceLength = (int)sourceValue.GetType().InvokeMember("Length", BindingFlags.GetProperty, null, sourceValue, null);
Array targetArray = Array.CreateInstance(piTarget.PropertyType.GetElementType(), sourceLength);
Array array = (Array)sourceField.GetValue(source, null);
for (int i = 0; i < array.Length; i++)
{
object o = array.GetValue(i);
object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetElementType());
CopyObjectData(o, tempTarget, "", memberAccess);
targetArray.SetValue(tempTarget, i);
}
piTarget.SetValue(target, targetArray, null);
}
new JavaScriptSerializer().Serialize(new NamespaceA.Person{Id = 1, Name = "A"})
返回
{Id: 1, Name: "A"}
通过这个,我们可以反序列化任何具有相同属性名称的类。
为了进行深度复制,我使用了Newtonsoft并创建了一个通用方法,例如:
public T DeepCopy<T>(T objectToCopy)
{
var objectSerialized = JsonConvert.SerializeObject(objectToCopy);
return JsonConvert.DeserializeObject<T>(objectSerialized);
}
我知道这不是很正统的解决方案,但它对我很有效。
/// <summary>
/// Copies matching object's properties from different type objects i.e from source object to destination Type T object
/// </summary>
/// <param name="source"></param>
/// <returns>New Type T object with copied property values</returns>
public static T CopyPropertiesTo<T>(this object source) where T: new()
{
var fromProperties = source.GetType().GetProperties();
var destination = new T();
var toProperties = destination.GetType().GetProperties();
foreach (var fromProperty in fromProperties)
{
var fromPropertyType = fromProperty.PropertyType;
if (Nullable.GetUnderlyingType(fromPropertyType) != null)
{
fromPropertyType = Nullable.GetUnderlyingType(fromPropertyType);
}
var toProperty = toProperties.FirstOrDefault(x => x.Name.Equals(fromProperty.Name, StringComparison.OrdinalIgnoreCase));
if (toProperty != null)
{
var toPropertyType = toProperty.PropertyType;
if (Nullable.GetUnderlyingType(toPropertyType) != null)
{
toPropertyType = Nullable.GetUnderlyingType(toPropertyType);
}
if (fromPropertyType == toPropertyType)
{
toProperty.SetValue(destination, fromProperty.GetValue(source));
}
}
}
return destination;
}