假设我有这个包含此方法的类Myclass:
public class MyClass
{
public int MyProperty { get; set; }
public int MySecondProperty { get; set; }
public MyOtherClass subClass { get; set; }
public object clone<T>(object original, T emptyObj)
{
FieldInfo[] fis = this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
object tempMyClass = Activator.CreateInstance(typeof(T));
foreach (FieldInfo fi in fis)
{
if (fi.FieldType.Namespace != original.GetType().Namespace)
fi.SetValue(tempMyClass, fi.GetValue(original));
else
fi.SetValue(tempMyClass, this.clone(fi.GetValue(original), fi.GetValue(original)));
}
return tempMyClass;
}
}
然后这个班:
public class MyOtherClass
{
public int MyProperty777 { get; set; }
}
当我这样做时:
MyClass a = new MyClass {
MyProperty = 1,
MySecondProperty = 2,
subClass = new MyOtherClass() { MyProperty777 = -1 }
};
MyClass b = a.clone(a, a) as MyClass;
如何第二次调用clone,T是object类型而不是MyOtherClass类型
答案 0 :(得分:3)
您对clone
的第二次(递归)调用将GetValue
的结果作为第二个参数传递,其类型为object
,因此T
为{{1} }}
即
object
fi.SetValue(tempMyClass, this.clone(fi.GetValue(original), fi.GetValue(original)));
上GetValue
的结果是FieldInfo
。
鉴于你在所有情况下都传递了两次相同的东西,object
方法的设计可能是错误的。你可能不需要泛型。只需使用clone
来获取第二个参数的类型信息(如果确实需要第二个参数)。
使用泛型约束返回类型会更有意义,因此在调用端不需要强制转换。您也可以将Clone变为扩展方法,以便它可以应用于任何内容。
另一方面,您尝试做的事情(自动深度克隆)通常不太可能有用。大多数类最终都会保存对他们不拥有的东西的引用,因此如果您克隆这样的对象,最终会意外地克隆一半的应用程序框架。
答案 1 :(得分:1)
试试这个:
public static class Cloner
{
public static T clone(this T item)
{
FieldInfo[] fis = item.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
object tempMyClass = Activator.CreateInstance(item.GetType());
foreach (FieldInfo fi in fis)
{
if (fi.FieldType.Namespace != item.GetType().Namespace)
fi.SetValue(tempMyClass, fi.GetValue(item));
else
{
object obj = fi.GetValue(item);
fi.SetValue(tempMyClass, obj.clone());
}
}
return (T)tempMyClass;
}
}
MyClass b = a.clone() as MyClass;
答案 2 :(得分:0)
首先,我同意克隆方法应该是静态的,但我不认为
object tempMyClass = Activator.CreateInstance(typeof(T));
是个好主意。我认为更好的方法是使用原始类型并完全摆脱emptyObject参数。
object tempMyClass = Activator.CreateInstance(original.GetType());
此外,您必须GetFields
original
而不是this
。
所以我的方法是
public static T clone<T>(T original)
{
T tempMyClass = (T)Activator.CreateInstance(original.GetType());
FieldInfo[] fis = original.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fi in fis)
{
object fieldValue = fi.GetValue(original);
if (fi.FieldType.Namespace != original.GetType().Namespace)
fi.SetValue(tempMyClass, fieldValue);
else
fi.SetValue(tempMyClass, clone(fieldValue));
}
return tempMyClass;
}
请注意,无论如何我使用original.GetType()
,因为内部调用无论如何都会有T = Object
类型。使用的泛型类型在编译时确定,并且Object
为fi.GetValue
的返回类型。
您可以将此静态方法移动到某个静态助手类。
作为旁注,我想说如果你的命名空间中的某个类中有一些集合类型字段(或任何标准的可变复合字段),那么“深度”克隆的实现将无法正常工作。 / p>
答案 3 :(得分:0)
克隆类实例的最佳方法是创建一个委托来完成它。实际上,linq表达式生成的委托可以访问私有/内部/受保护和公共字段。委托只能创建一次。将它保存在泛型类的静态字段中,以利用通用查找解析而不是字典
/// <summary>
/// Help to find metadata from expression instead of string declaration to improve reflection reliability.
/// </summary>
static public class Metadata
{
/// <summary>
/// Identify method from method call expression.
/// </summary>
/// <typeparam name="T">Type of return.</typeparam>
/// <param name="expression">Method call expression.</param>
/// <returns>Method.</returns>
static public MethodInfo Method<T>(Expression<Func<T>> expression)
{
return (expression.Body as MethodCallExpression).Method;
}
}
/// <summary>
/// Help to find metadata from expression instead of string declaration to improve reflection reliability.
/// </summary>
/// <typeparam name="T">Type to reflect.</typeparam>
static public class Metadata<T>
{
/// <summary>
/// Cache typeof(T) to avoid lock.
/// </summary>
static public readonly Type Type = typeof(T);
/// <summary>
/// Only used as token in metadata expression.
/// </summary>
static public T Value { get { throw new InvalidOperationException(); } }
}
/// <summary>
/// Used to clone instance of any class.
/// </summary>
static public class Cloner
{
/// <summary>
/// Define cloner implementation of a specific type.
/// </summary>
/// <typeparam name="T">Type to clone.</typeparam>
static private class Implementation<T>
where T : class
{
/// <summary>
/// Delegate create at runtime to clone.
/// </summary>
static public readonly Action<T, T> Clone = Cloner.Implementation<T>.Compile();
/// <summary>
/// Way to emit delegate without static constructor to avoid performance issue.
/// </summary>
/// <returns>Delegate used to clone.</returns>
static public Action<T, T> Compile()
{
//Define source and destination parameter used in expression.
var _source = Expression.Parameter(Metadata<T>.Type);
var _destination = Expression.Parameter(Metadata<T>.Type);
//Clone method maybe need more than one statement.
var _body = new List<Expression>();
//Clone all fields of entire hierarchy.
for (var _type = Metadata<T>.Type; _type != null; _type = _type.BaseType)
{
//Foreach declared fields in current type.
foreach (var _field in _type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))
{
//Assign destination field using source field.
_body.Add(Expression.Assign(Expression.Field(_destination, _field), Expression.Field(_source, _field)));
}
}
//Compile expression to provide clone method.
return Expression.Lambda<Action<T, T>>(Expression.Block(_body), _source, _destination).Compile();
}
}
/// <summary>
/// Keep instance of generic definition of clone method to improve performance in reflection call case.
/// </summary>
static private readonly MethodInfo Method = Metadata.Method(() => Cloner.Clone(Metadata<object>.Value)).GetGenericMethodDefinition();
static public T Clone<T>(T instance)
where T : class
{
//Nothing to clone.
if (instance == null) { return null; }
//Identify instace type.
var _type = instance.GetType();
//if T is an interface, instance type might be a value type and it is not needed to clone value type.
if (_type.IsValueType) { return instance; }
//Instance type match with generic argument.
if (_type == Metadata<T>.Type)
{
//Instaitate clone without executing a constructor.
var _clone = FormatterServices.GetUninitializedObject(_type) as T;
//Call delegate emitted once by linq expreesion to clone fields.
Cloner.Implementation<T>.Clone(instance, _clone);
//Return clone.
return _clone;
}
//Reflection call case when T is not target Type (performance overhead).
return Cloner.Method.MakeGenericMethod(_type).Invoke(null, new object[] { instance }) as T;
}
}
答案 4 :(得分:0)
我尝试使用此处发布的示例克隆实体框架对象,但无济于事。
我用另一种方法制作了扩展方法,现在可以克隆EF对象:
public static T CloneObject<T>(this T source)
{
if (source == null || source.GetType().IsSimple())
return source;
object clonedObj = Activator.CreateInstance(source.GetType());
var properties = source.GetType().GetProperties();
foreach (var property in properties)
{
try
{
property.SetValue(clonedObj, property.GetValue(source));
}
catch { }
}
return (T)clonedObj;
}
public static bool IsSimple(this Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// nullable type, check if the nested type is simple.
return IsSimple(type.GetGenericArguments()[0]);
}
return !type.IsClass
|| type.IsPrimitive
|| type.IsEnum
|| type.Equals(typeof(string))
|| type.Equals(typeof(decimal));
}
我没有检查数组的大小写,但是您也可以为此添加一些代码(例如在link中):
else if (type.IsArray)
{
Type typeElement = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copiedArray = Array.CreateInstance(typeElement, array.Length);
for (int i = 0; i < array.Length; i++)
{
// Get the deep clone of the element in the original array and assign the
// clone to the new array.
copiedArray.SetValue(CloneProcedure(array.GetValue(i)), i);
}
return copiedArray;
}