代码之家  ›  专栏  ›  技术社区  ›  Jon Simpson

如何在C#中不同类型的对象之间进行深度复制。网

  •  10
  • Jon Simpson  · 技术社区  · 17 年前

    模板ClassV1和ClassV2之间的继承性被排除在外,因为这两个类需要独立进化。我考虑过使用反射(速度较慢)和二进制序列化(速度也较慢)来执行公共属性的映射。

    是否有首选方法?还有其他选择吗?

    9 回复  |  直到 17 年前
        1
  •  6
  •   Chris Ballard    17 年前

    作为反思。Emit相当笨重,我建议去看看 this Reflector插件,它非常适合构建这种代码。

        2
  •  4
  •   Marc Gravell    14 年前

    什么版本的。NET是吗?

    对于浅拷贝:

    在3.5中,您可以预编译 Expression 为了做到这一点。在2.0版本中,您可以使用 HyperDescriptor 很容易做到这一点。两者都将远远超过反思。

    表达 方法在 MiscUtil - PropertyCopy :

    DestType clone = PropertyCopy<DestType>.CopyFrom(original);
    

    BinaryFormatter(在问题中)在这里不是一个选项——它根本无法工作,因为原始类型和目标类型不同。如果数据是基于合约的,则XmlSerializer或DataContractSerializer可以工作 如果 所有合同名称都匹配,但如果可能的话,上述两个(浅)选项会快得多。

    此外,如果您的类型标记了通用序列化属性( XmlType DataContract )那么 protobuf-net (在某些情况下)为您进行深度复制/更改类型:

    DestType clone = Serializer.ChangeType<OriginalType, DestType>(original);
    

    但这取决于具有非常相似模式的类型(事实上,它不使用名称,而是在属性上使用显式的“Order”等)

        3
  •  3
  •   Chris Pitman    16 年前

    你可能想看看 AutoMapper ,一个专门在对象之间复制值的库。它使用约定而非配置,因此,如果属性确实具有exaxt相同的名称,它将为您完成几乎所有的工作。

        4
  •  2
  •       16 年前

    以下是我构建的一个解决方案:

         /// <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>
            public static void CopyObjectData(object source, object target)
            {
                CopyObjectData(source, target, String.Empty, BindingFlags.Public | 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</param>
            /// <param name="excludedProperties">A comma delimited list of properties that should not be copied</param>
            /// <param name="memberAccess">Reflection binding access</param>
            public static void CopyObjectData(object source, object target, string excludedProperties, BindingFlags memberAccess)
            {
                string[] excluded = null;
                if (!string.IsNullOrEmpty(excludedProperties))
                {
                    excluded = excludedProperties.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                }
    
                MemberInfo[] miT = target.GetType().GetMembers(memberAccess);
                foreach (MemberInfo Field in miT)
                {
                    string name = Field.Name;
    
                    // Skip over excluded properties
                    if (string.IsNullOrEmpty(excludedProperties) == false
                        && excluded.Contains(name))
                    {
                        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);
                    }
                    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)
                        {
                            object targetValue = piTarget.GetValue(target, null);
                            object sourceValue = sourceField.GetValue(source, null);
    
                            if (sourceValue == null) { continue; }
    
                            if (sourceField.PropertyType.IsArray
                                && piTarget.PropertyType.IsArray
                                && sourceValue != null ) 
                            {
                                CopyArray(source, target, memberAccess, piTarget, sourceField, sourceValue);
                            }
                            else
                            {
                                CopySingleData(source, target, memberAccess, piTarget, sourceField, targetValue, sourceValue);
                            }
                        }
                    }
                }
            }
    
            private static void CopySingleData(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object targetValue, object sourceValue)
            {
                //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, "", memberAccess);
                    piTarget.SetValue(target, targetValue, null);
                }
                else
                {
                    if (piTarget.PropertyType.FullName == sourceField.PropertyType.FullName)
                    {
                        object tempSourceValue = sourceField.GetValue(source, null);
                        piTarget.SetValue(target, tempSourceValue, null);
                    }
                    else
                    {
                        CopyObjectData(piTarget, target, "", memberAccess);
                    }
                }
            }
    
            private static void CopyArray(object source, object target, BindingFlags memberAccess, PropertyInfo piTarget, PropertyInfo sourceField, object sourceValue)
            {
                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, "", memberAccess);
                    targetArray.SetValue(tempTarget, i);
                }
                piTarget.SetValue(target, targetArray, null);
            }
    
        5
  •  1
  •   Simon Steele    17 年前

    如果速度是一个问题,您可以将反射过程脱机,并生成用于映射公共属性的代码。您可以在运行时使用轻量级代码生成或完全离线构建C#代码进行编译。

        6
  •  1
  •   Alex Shirshov    16 年前

    如果您控制目标对象的实例化,请尝试使用

    new JavaScriptSerializer().Serialize(new NamespaceA.Person{Id = 1, Name = "A"})
    

    回报

    {Id: 1, Name: "A"}
    

        7
  •  0
  •   Øyvind Skaar    17 年前

    如果速度是一个问题,您应该在方法本身中实现克隆方法。