using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace System
{
    
public static class CloneHelper
    {
        
private static T CreatePrototype<T>(T obj)
            
where T : class
        {
            
if (obj == null)
                
throw new ArgumentNullException("obj");

            var type 
= obj.GetType();
            
if (!type.IsArray)
                
return (T)Activator.CreateInstance(type);

            var array 
= (Array)(object)obj;
            var length 
= array.Length;
            var eType 
= type.GetElementType();
            
return (T)(object)Array.CreateInstance(eType, length);
        }

        
/// <summary>
        
/// This method uses reflection to copy all field values.
        
/// </summary>
        public static T DeepClone<T>(this T obj)
            
where T : class
        {
            
if (obj == null)
                
return null;

            var newValue 
= CreatePrototype(obj);
            CopyFields(obj, newValue, 
true);
            
return newValue;
        }

        
/// <summary>
        
/// This method uses reflection to copy all field values.
        
/// </summary>
        public static T ShallowClone<T>(this T obj)
            
where T : class
        {
            
if (obj == null)
                
return null;
            var newValue 
= CreatePrototype(obj);
            CopyFields(obj, newValue, 
false);
            
return newValue;
        }

        
/// <summary>
        
/// <para>This method uses reflection to copy all field values.</para>
        
/// <para>Remark: only support SZ array, other array types are not supported.</para>
        
/// </summary>
        public static void CopyFields<T>(T source, T dest, bool deepCopy)
            
where T : class
        {
            
if (source == null || dest == null)
                
throw new ArgumentNullException();


            var type 
= source.GetType();
            
if (type != dest.GetType())
                
throw new ArgumentException("source and dest is not the same type.");

            
if (type.IsArray)
            {
                CopyArray((Array)(
object)source, (Array)(object)dest, type.GetElementType(), deepCopy);
            }
            
else
            {
                var fields 
= type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) as IEnumerable<FieldInfo>;
                
while (type.BaseType != typeof(object))
                {
                    type 
= type.BaseType;
                    fields 
= fields.Concat(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
                }
                
foreach (var field in fields)
                    CopyField(source, dest, field, deepCopy);
            }
        }

        
private static void CopyArray(Array source, Array dest, Type elementType, bool deepCopy)
        {
            var length 
= source.Length;
            
if (length != dest.Length)
                
throw new ArgumentOutOfRangeException("dest""Array source and dest don't have the same size.");

            
for (var i = 0; i < length; i++)
            {
                var oldValue 
= source.GetValue(i);
                
if (!deepCopy || oldValue == null || elementType.IsValueType || elementType == typeof(string))
                    dest.SetValue(oldValue, i);
                
else
                    dest.SetValue(oldValue.DeepClone(), i);
            }
        }

        
private static void CopyField<T>(T source, T dest, FieldInfo field, bool deepCopy)
        {
            var oldValue 
= field.GetValue(source);
            var fieldType 
= field.FieldType;

            
if (!deepCopy || oldValue == null || fieldType.IsValueType || fieldType == typeof(string))
            {
                field.SetValue(dest, oldValue);
                
return;
            }

            var oldType 
= oldValue.GetType();
            var newValue 
= CreatePrototype(oldValue);
            var oldArray 
= oldValue as Array;

            
if (oldArray == null)
                CopyFields(oldValue, newValue, deepCopy);
            
else
                CopyArray(oldArray, (Array)newValue, oldType.GetElementType(), deepCopy);

            field.SetValue(dest, newValue);
        }

        
/// <summary>
        
/// This method uses reflection to copy all NON-INDEXED property values.
        
/// </summary>
        public static void CopyProperties<T>(T source, T dest, bool deepCopy)
            
where T : class
        {
            
if (source == null || dest == null)
                
throw new ArgumentNullException();

            var type 
= source.GetType();
            
if (type != dest.GetType())
                
throw new ArgumentException("source and dest is not the same type.");

            
if (type.IsArray)
            {
                CopyArray((Array)(
object)source, (Array)(object)dest, type.GetElementType(), deepCopy);
            }
            
else
            {
                var properties 
= type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                
foreach (var property in properties)
                    
if (property.CanRead && property.CanWrite)
                        CopyProperty(source, dest, property, deepCopy);
            }
        }

        
private static void CopyProperty<T>(T source, T dest, PropertyInfo property, bool deepCopy)
        {
            var oldValue 
= property.GetValue(source, null);
            var fieldType 
= property.PropertyType;

            
if (!deepCopy || oldValue == null || fieldType.IsValueType || fieldType == typeof(string))
            {
                property.SetValue(dest, oldValue, 
null);
                
return;
            }

            var oldType 
= oldValue.GetType();
            var newValue 
= CreatePrototype(oldValue);
            var oldArray 
= oldValue as Array;

            
if (oldArray == null)
                CopyFields(oldValue, newValue, deepCopy);
            
else
                CopyArray(oldArray, (Array)newValue, oldType.GetElementType(), deepCopy);

            property.SetValue(dest, newValue, 
null);
        }
    }
}

相关文章:

  • 2021-09-19
  • 2021-09-02
  • 2022-12-23
  • 2021-11-12
  • 2021-12-13
  • 2022-12-23
  • 2021-07-18
猜你喜欢
  • 2021-10-05
  • 2021-11-16
  • 2021-07-26
  • 2021-11-17
  • 2021-09-18
  • 2022-12-23
  • 2021-10-01
相关资源
相似解决方案