【问题标题】:Deep copy of objects using System.Reflection?使用 System.Reflection 的对象的深层副本?
【发布时间】:2016-02-15 06:22:03
【问题描述】:

如何在 C# 中使用 System.Reflection 对对象进行深拷贝?

【问题讨论】:

  • 为什么需要Reflection?以及到目前为止您尝试过的内容
  • 属性/字段的递归。问题是要包含哪一个,如何处理私有字段等。因此,Clone 通过序列化现有对象和反序列化新对象,更容易处理专门为序列化对象准备的对象。

标签: c# .net reflection deep-copy


【解决方案1】:

一种简单的方法是使用 JSON:

public static T DeepClone<T>(T source)
{
    var serialized = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(serialized);
}

它为你做反射。显然,它不适用于任何具有非托管对象句柄等的东西。

(您可以使用 NuGet 将 Newtonsoft.Json 安装到您的项目中。)

默认情况下,Json 不会序列化私有字段。

你可以这样解决:

public static T DeepClone<T>(T source)
{
    var settings = new JsonSerializerSettings {ContractResolver = new MyContractResolver()};
    var serialized = JsonConvert.SerializeObject(source, settings);
    return JsonConvert.DeserializeObject<T>(serialized);
}

public class MyContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                        .Select(p => base.CreateProperty(p, memberSerialization))
                    .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .Select(f => base.CreateProperty(f, memberSerialization)))
                    .ToList();
        props.ForEach(p => { p.Writable = true; p.Readable = true; });
        return props;
    }
}

这是一个完整的控制台应用示例,展示了如何克隆具有私有字段的任意类。请注意,Json 尝试使用构造函数来设置字段和/或属性,如果构造函数参数名称与字段或属性名称不匹配,它将无法正常工作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace ConsoleApplication1
{
    class Test
    {
        public Test(double y, string s, int x)
        {
            this.Y = y;
            this.s = s;
            this.X = x;
        }

        public int X;

        public double Y { get; private set; }

        public string Z         
        {
            get
            {
                return s;
            }
        }

        private string s;
    }

    class Program
    {
        static void Main()
        {
            var test = new Test(1.2345, "12345", 12345);
            test.X = 12345;

            var copy = DeepClone(test);

            Console.WriteLine("X = " + copy.X);
            Console.WriteLine("Y = " + copy.Y);
            Console.WriteLine("Z = " + copy.Z);
        }

        public static T DeepClone<T>(T source)
        {
            var settings = new JsonSerializerSettings {ContractResolver = new MyContractResolver()};
            var serialized = JsonConvert.SerializeObject(source, settings);
            return JsonConvert.DeserializeObject<T>(serialized);
        }

        public class MyContractResolver : DefaultContractResolver
        {
            protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
            {
                var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                .Select(p => base.CreateProperty(p, memberSerialization))
                            .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                                        .Select(f => base.CreateProperty(f, memberSerialization)))
                            .ToList();
                props.ForEach(p => { p.Writable = true; p.Readable = true; });
                return props;
            }
        }
    }
}

【讨论】:

  • 这不适用于任何对象。对象必须准备好进行序列化。
  • @Sinatr 它适用于许多对象 - 如果您需要使用递归,那么无论如何该对象都必须是可序列化的。
  • 我的意思是你必须要么创建特殊的公共属性来携带对象状态,要么用属性标记某些私有字段(例如)。那么Json还不错,但是我不确定这个选择是不是最好的(fastest?)。
  • Bravo @MatthewWatson 它正在工作。只需确保 copy.Equals(test) =false。就是这样
猜你喜欢
  • 2015-12-28
  • 1970-01-01
  • 2011-12-09
  • 2013-04-09
  • 2022-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多