我正在尝试深层复制复杂对象
public class Team
{
public string id { get; set; }
public Driver driver{ get; set;}
public Driver codriver{ get; set;}
}
public class Driver
{
public Team parentTeam{ get; set;}
public string driverId { get; set; }
}
var modifiedTeams = new List<Team>
{
new Team {id = "T1", driver = new Driver { driverId = "D2" }, codriver = new Driver { driverId = "C1"} },
new Team {id = "T2", driver = new Driver { driverId = "D1"} }
};
//注意:父团队持有对驱动程序或codriver所在团队的引用
我想深度复制修改后的列表。以下是我正在使用的实用程序。
/// <summary>
/// Utility
/// </summary>
public static class DeepCopyUtility
{
private static string[] _excludedPropertyNames = null;
private static BindingFlags _memberAccess = (BindingFlags.Public | BindingFlags.NonPublic | 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 public</param>
/// <param name="propertyNames"> </param>
public static void DeepCopy<T>(T source, T target, string[] propertyNames = null)
{
if (source == null)
{
throw new ArgumentNullException("Object is null");
}
if (propertyNames != null)
{
_excludedPropertyNames = propertyNames;
}
CopyObjectData(source, target, new Dictionary<object, object>());
}
/// <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>
public static void CopyObjectData(object source, object target, Dictionary<object, object> cloned)
{
Type type = source.GetType();
if (target == null && type.IsValueType == false && type != typeof(string))
{
if (source.GetType().IsArray)
{
target = Activator.CreateInstance(source.GetType().GetElementType());
}
else if (source.GetType().IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
if (typeof(IList).IsAssignableFrom(type))
{
target = (IList)Activator.CreateInstance(type);
}
else if (type.IsGenericType)
{
var objectType = type.GetGenericArguments().Single();
if (typeof(IList<>).MakeGenericType(objectType).IsAssignableFrom(type) || typeof(ISet<>).MakeGenericType(objectType).IsAssignableFrom(type))
{
target = Activator.CreateInstance(type);
}
}
}
else
{
target = Activator.CreateInstance(source.GetType());
}
}
MemberInfo[] miT = target.GetType().GetMembers(_memberAccess);
foreach (MemberInfo field in miT)
{
string name = field.Name;
// Skip over excluded properties
if (_excludedPropertyNames != null && _excludedPropertyNames.Contains(name))
{
continue;
}
object clone;
if (cloned.TryGetValue(source, out clone))
{
// this object has been cloned earlier, return reference to that clone
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);
cloned[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)
{
if (sourceField.PropertyType.IsArray && piTarget.PropertyType.IsArray)
{
CopyArray(source, target, piTarget, sourceField, cloned);
}
else if ((sourceField.PropertyType.IsGenericType && sourceField.PropertyType.GetGenericTypeDefinition() == typeof(List<>))
&& (piTarget.PropertyType.IsGenericType && piTarget.PropertyType.GetGenericTypeDefinition() == typeof(List<>)))
{
CopyList(source, target, piTarget, sourceField, cloned);
}
else
{
CopySingleData(source, target, piTarget, sourceField, cloned);
}
}
}
}
}
private static void CopySingleData(object source, object target, PropertyInfo piTarget, PropertyInfo sourceField, Dictionary<object, object> cloned)
{
object sourceValue = sourceField.GetValue(source, null);
object targetValue = piTarget.GetValue(target, null);
if (sourceValue == null) { return; }
//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, cloned);
piTarget.SetValue(target, targetValue, null);
}
else
{
if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
{
object tempSourceValue = sourceField.GetValue(source, null);
piTarget.SetValue(target, tempSourceValue, null);
cloned[target] = tempSourceValue;
}
else
{
CopyObjectData(piTarget, target, cloned);
}
}
}
private static void CopyArray(object source, object target, PropertyInfo piTarget, PropertyInfo sourceField, Dictionary<object, object> cloned)
{
object sourceValue = sourceField.GetValue(source, null);
if (sourceValue == null) { return; }
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, cloned);
targetArray.SetValue(tempTarget, i);
}
piTarget.SetValue(target, targetArray, null);
cloned[target] = targetArray;
}
private static void CopyList(object source, object target, PropertyInfo piTarget, PropertyInfo sourceField, Dictionary<object, object> cloned)
{
var sourceValue = ((IEnumerable)sourceField.GetValue(source, null)).Cast<object>().ToList();
if (!sourceValue.Any()) { return; }
int sourceLength = (int)sourceValue.GetType().GetProperty("Count").GetValue(sourceValue);
var listType = typeof(List<>);
Type[] genericArgs = sourceField.PropertyType.GetGenericArguments();
var concreteType = listType.MakeGenericType(genericArgs);
var newList = (IList)Activator.CreateInstance(concreteType);
var obj = (IList)sourceField.GetValue(source, null);
for (int i = 0; i < sourceLength; i++)
{
object[] ind = { i };
object o = obj[i];
object tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetGenericArguments()[0]);
CopyObjectData(o, tempTarget, cloned);
newList.Insert(i, tempTarget);
}
piTarget.SetValue(target, newList, null);
cloned[target] = newList;
}
执行时我收到“System.Reflection.TargetParameterCountException:Parameter count mismatch。”。这主要是因为它试图从团队列表中获取团队价值。有人可以帮我解决这个问题。
注意:此实用程序代码参考以下链接: - https://ddkonline.blogspot.com/2010/04/net-deep-copy-between-2-different.html?_sm_au_=iVV8TZZ5L71MRvZf
答案 0 :(得分:0)
你提到你对其他形式的克隆持开放态度,所以这里有一个建议。
如果您可以使用Json.NET,那么您可以利用其序列化来执行克隆操作,如下所示:
public static class Cloner
{
public static T Clone<T>(T source)
{
if (ReferenceEquals(source, null))
return default(T);
var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings);
}
class ContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
}
测试项目(控制台应用):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Demo
{
public class Team
{
public string id { get; set; }
public Driver driver { get; set; }
public Driver codriver { get; set; }
}
public class Driver
{
public Team parentTeam { get; set; }
public string driverId { get; set; }
}
class Program
{
static void Main()
{
var original = new List<Team>
{
new Team {id = "T1", driver = new Driver { driverId = "D2" }, codriver = new Driver { driverId = "C1"} },
new Team {id = "T2", driver = new Driver { driverId = "D1"} }
};
var cloned = Cloner.Clone(original);
// Change original to prove that clone is not referencing it.
original[0].codriver.driverId = "changed";
Console.WriteLine(cloned[0].codriver.driverId); // Should be C1
}
}
public static class Cloner
{
public static T Clone<T>(T source)
{
if (ReferenceEquals(source, null))
return default(T);
var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings);
}
class ContractResolver : DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(p => base.CreateProperty(p, memberSerialization))
.Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(f => base.CreateProperty(f, memberSerialization)))
.ToList();
props.ForEach(p => { p.Writable = true; p.Readable = true; });
return props;
}
}
}
}
注意强>
有关上述克隆代码的原始来源,请参阅this answer。