vendredi 17 novembre 2017

c# Deep Copy reflection GetMembers

I am trying to deep copy a complex object

public class Team
{
    public string Id { get; set; }
    Driver driver{ get; set;}
    Driver codriver{ get; set;}
}

public class Driver
{
    Team parentTeam{ get; set;}
    public string DriverId { get; set; }
}

var modifiedTeams = new List<Team>
{
    new Team {Id = "T1", Driver = new Driver { ParentTeam = "T1", DriverId = "D2" }, Codriver = new Driver { ParentTeam = "T1", DriverId = "C1"} },
    new Team {Id = "T2", Driver = new Driver { ParentTeam = "T2", DriverId = "D1"} }
};

I want to deep copy the modified lists. Below is the utility that I am using.

    /// <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;
    }

On execution I am getting "System.Reflection.TargetParameterCountException : Parameter count mismatch.". This is mainly because it is trying to get Team value from a list of teams. Can someone help me with how I could fix this.

Note: This utility code is referred from below links:- http://ift.tt/2AWKJYw

http://ift.tt/2yThvZj





Aucun commentaire:

Enregistrer un commentaire