我正在编写C#表单应用程序,我正在使用以下代码克隆对象:
public static class ObjectExtension
{
public static object CloneObject(this object objSource)
{
//Get the type of source object and create a new instance of that type
Type typeSource = objSource.GetType();
object objTarget = Activator.CreateInstance(typeSource);
//Get all the properties of source object type
PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Assign all source property to taget object 's properties
foreach (PropertyInfo property in propertyInfo)
{
//Check whether property can be written to
if (property.CanWrite)
{
//check whether property type is value type, enum or string type
if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
{
property.SetValue(objTarget, property.GetValue(objSource, null), null);
}
//else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
else
{
object objPropertyValue = property.GetValue(objSource, null);
if (objPropertyValue == null)
{
property.SetValue(objTarget, null, null);
}
else
{
property.SetValue(objTarget, objPropertyValue.CloneObject(), null);
}
}
}
}
return objTarget;
}
}
我的一些对象有父对象,这些父对象有集合。因此,我得到以下例外:
未处理的类型' System.StackOverflowException' 发生在CustomWebpageObjectCreator.exe
中
如何防止这种情况发生?
是否可以使用属性修饰对象中的某些特定属性,以便CloneObject代码不会尝试克隆该属性?如果是这样,有人可以帮我提供代码吗?如果没有,我应该如何修改我的代码?
答案 0 :(得分:1)
您可以使用字典来记忆所有原始实例以克隆它:
public static class ObjectExtension
{
private static readonly MethodInfo MemberwiseCloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
public static object CloneObject(this object objSource)
{
return InnerCloneObject(objSource, new Dictionary<object, object>(ObjectReferenceEqualityComparer<object>.Default)));
}
private static object InnerCloneObject(this object objSource, IDictionary<object, object> alreadyVisitedInstances)
{
if (objSource == null)
{
return null;
}
var instanceType = objSource.GetType();
if (IsPrimitive(instanceType) || instanceType.IsMarshalByRef)
{
return objSource;
}
object clonedInstance;
if (alreadyVisitedInstances.TryGetValue(objSource, out clonedInstance))
{
// We already have a clone...
return clonedInstance;
}
if (typeof(Delegate).IsAssignableFrom(instanceType))
{
// Copying delegates is very hard...
return null;
}
// Don't use activator because it needs a parameterless constructor.
objTarget = MemberwiseCloneMethod.Invoke(objSource, null);
alreadyVisitedInstances.Add(objSource, objTarget);
//Get all the properties of source object type
PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Assign all source property to taget object 's properties
foreach (PropertyInfo property in propertyInfo.Where(x => x.CanWrite)
{
property.SetValue(objTarget, property.GetValue(objSource, null).InnerCloneObject(alreadyVisitedInstances));
}
return objTarget;
}
private static bool IsPrimitive(Type type)
{
return type == typeof(string) || type.IsPointer || (type.IsValueType && type.IsPrimitive);
}
}
在我们创建新实例之前,我们会检查是否已有克隆。关键是在复制所有属性之前将这个新的实例添加到字典中。
请注意我更改了您的代码以使用MemberwiseClone
创建没有无参数构造函数的类实例。
它使用这个比较器:
public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T>, IEqualityComparer
where T : class
{
private static ObjectReferenceEqualityComparer<T> _defaultComparer;
public static ObjectReferenceEqualityComparer<T> Default
{
get { return _defaultComparer ?? (_defaultComparer = new ObjectReferenceEqualityComparer<T>()); }
}
public bool Equals(T x, T y)
{
return object.ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
bool IEqualityComparer.Equals(object x, object y)
{
return this.Equals((T)x, (T)y);
}
int IEqualityComparer.GetHashCode(object obj)
{
return this.GetHashCode((T)obj);
}
}
您必须记住,复制数组需要单独的案例处理。
小心使用此功能。如果你像流一样复制一个系统对象,你就会创建像Encodings这样的单例副本。我已经完成了这个并且遇到了一些麻烦。