2010-04-14 83 views
1

说我有这个类MYCLASS包含此方法:C#反射,克隆

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; 

怎么来的在第二次调用克隆时,T是类型对象,而不是类型MyOtherClass

+0

您指的是'clone'的哪个调用? – 2010-04-14 17:35:51

回答

3

clone的第二次(递归)调用传递GetValue作为第二个参数的结果,该参数的类型为object,因此Tobject

fi.SetValue(tempMyClass, this.clone(fi.GetValue(original), fi.GetValue(original))); 

GetValue上的FieldInfo结果是一个object

鉴于您在所有情况下都通过相同的事情两次,clone方法的设计可能是错误的。你可能不需要那里的泛型。只需使用obj.GetType()即可获取第二个参数的类型信息(如果确实需要第二个参数)。

使用泛型约束返回类型会更有意义,因此在调用方不需要强制转换。你也可以将克隆变成扩展方法,这样它就可以应用于任何事情。

另一方面,你试图做的事情(自动深度克隆)不太可能有用。大多数类最终拥有它们不拥有的东西的引用,所以如果你克隆了这样一个对象,最终会意外地克隆你的一半应用程序框架。

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; 
0

首先,我认为,克隆方法应该是静态的,但我不认为

object tempMyClass = Activator.CreateInstance(typeof(T)); 

是一个好主意。我认为更好的方法是使用原始类型并根本除掉emptyObject参数。

object tempMyClass = Activator.CreateInstance(original.GetType()); 

你也需要GetFieldsoriginal不是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

您可以将此静态方法移动到某个静态帮助器类。

作为一个方面说明,我想说的是,如果在您的名称空间的某个类中存在某个集合类型字段(或任何标准的可变复合字段),则“深度”克隆的实现将无法正常工作。

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; 
    } 
} 
+0

你正在做的事情的简要说明将有助于提问者更好地理解 – 2015-06-10 01:15:51

+0

克隆类实例的最佳方法是创建一个委托来完成它。事实上,由linq表达式产生的代表可以访问私有/内部/受保护和公共领域。代表只能创建一次。将其保留在泛型类中的静态字段中,以利用通用查找分辨率而不是字典。 – 2015-06-10 01:29:30

+0

请编辑您的答案并添加您的解释。 – Gosu 2015-06-10 01:34:28