星峰得 2022-11-07 19:45 采纳率: 66.7%
浏览 66
已结题

解惑,请教带字典的对象不能深拷贝的问题

最近我在琢磨深拷贝时候遇到了带字典的对象不能深拷贝的问题,请各位帮我解惑下,感激不尽。
这是我的深拷贝方法

public class DeepCopy
    {
        public static T DeepCopyByBin<T>(T obj)
        {
            object retval;//序列化深拷贝
            using (MemoryStream ms = new MemoryStream()) //创建内存流 
            {
                XmlSerializer bf = new XmlSerializer(typeof(Box));  
                //序列化成流
                bf.Serialize(ms, obj);
                //反序列化当前实例到一个object 
                ms.Seek(0, SeekOrigin.Begin);
                retval = bf.Deserialize(ms);
                ms.Close();//关闭内存流            

            }
            return (T)retval;
        }
    }

这是我的对象和主方法

[Serializable]
   public  class Box
    {
        public double length;   // 长度
        public double breadth;  // 宽度
        public double height;   // 高度
        public Dictionary<string, Box> Properties { get; set; }
        public Box DeepCopy()
        {
            Box ret = new Box();
            ret.length = this.length;
            ret.breadth = this.breadth;
            ret.height = this.height;
            return ret;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Box b1 = new Box();
            Box b3 = new Box();
            b1.length = 1;
            b1.breadth = 1;
            b1.height = 1;
            b1.Properties = new Dictionary<string, Box>();
            b1.Properties.Add("xfd", b1);
            Box b2 = DeepCopy.DeepCopyByBin(b1);
            b2.breadth = 2;
            List<Box> list = new List<Box>();
            Console.WriteLine(b1.breadth);
            Console.WriteLine(b2.breadth);
            Console.ReadKey();
        }
    }

但是在调试的时候会报错,说字典不能序列化

报错文本:内部异常 1:
NotSupportedException: Cannot serialize member 序列化实现深拷贝.Box.Properties of type System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[序列化实现深拷贝.Box, 序列化实现深拷贝, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], because it implements IDictionary.

  • 写回答

3条回答 默认 最新

  • 码老头 2022-11-08 09:41
    关注

    这里提供一个可以深拷贝字典的示例:

    using Newtonsoft.Json;
    namespace DemoApp1;
    
    internal class Program
    {
        private static void Main(string[] args)
        {
            var b1 = new Box();
            var b3 = new Box();
            b1.Length = 1;
            b1.Breadth = 1;
            b1.Height = 1;
            b1.Properties = new Dictionary<string, Box> { { "xfd", b3 } };
            var b2 = b1.Copy();
            b2.Breadth = 2;
            var list = new List<Box>();
            Console.WriteLine(b1.Breadth);
            Console.WriteLine(b2.Breadth);
            list.AddRange(new[] { b1, b2, b3 });
            Console.WriteLine(JsonConvert.SerializeObject(list));
            Console.ReadKey();
        }
    }
    public class Box
    {
        public double Length;   // 长度
        public double Breadth;  // 宽度
        public double Height;   // 高度
        public Dictionary<string, Box> Properties { get; set; }
    }
    

    运行结果:

    1
    2
    [{"Length":1.0,"Breadth":1.0,"Height":1.0,"Properties":{"xfd":{"Length":0.0,"Breadth":0.0,"Height":0.0,"Properties":null}}},{"Length":1.0,"Breadth":2.0,"Height":1.0,"Properties":{"xfd":{"Length":0.0,"Breadth":0.0,"Height":0.0,"Properties":null}}},{"Length":0.0,"Breadth":0.0,"Height":0.0,"Properties":null}]
    

    ObjectExtensions.cs类和相关的静态扩展方法如下:

    using DemoApp1.ArrayExtensions;
    using System.Reflection;
    
    namespace DemoApp1
    {
        public static class ObjectExtensions
        {
            private static readonly MethodInfo CloneMethod = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
    
            public static bool IsPrimitive(this Type type)
            {
                if (type == typeof(string)) return true;
                return (type.IsValueType & type.IsPrimitive);
            }
    
            public static object Copy(this object originalObject)
            {
                return InternalCopy(originalObject, new Dictionary<object, object>(new ReferenceEqualityComparer()));
            }
            private static object InternalCopy(object originalObject, IDictionary<object, object> visited)
            {
                if (originalObject == null) return null;
                var typeToReflect = originalObject.GetType();
                if (IsPrimitive(typeToReflect)) return originalObject;
                if (visited.ContainsKey(originalObject)) return visited[originalObject];
                if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
                var cloneObject = CloneMethod.Invoke(originalObject, null);
                if (typeToReflect.IsArray)
                {
                    var arrayType = typeToReflect.GetElementType();
                    if (IsPrimitive(arrayType) == false)
                    {
                        var clonedArray = (Array)cloneObject;
                        clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
                    }
    
                }
                visited.Add(originalObject, cloneObject);
                CopyFields(originalObject, visited, cloneObject, typeToReflect);
                RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect);
                return cloneObject;
            }
    
            private static void RecursiveCopyBaseTypePrivateFields(object originalObject, IDictionary<object, object> visited, object cloneObject, Type typeToReflect)
            {
                if (typeToReflect.BaseType == null) return;
                RecursiveCopyBaseTypePrivateFields(originalObject, visited, cloneObject, typeToReflect.BaseType);
                CopyFields(originalObject, visited, cloneObject, typeToReflect.BaseType, BindingFlags.Instance | BindingFlags.NonPublic, info => info.IsPrivate);
            }
    
            private static void CopyFields(object originalObject, IDictionary<object, object> visited, object cloneObject, IReflect typeToReflect, BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy, Func<FieldInfo, bool> filter = null)
            {
                foreach (var fieldInfo in typeToReflect.GetFields(bindingFlags))
                {
                    if (filter != null && filter(fieldInfo) == false) continue;
                    if (IsPrimitive(fieldInfo.FieldType)) continue;
                    var originalFieldValue = fieldInfo.GetValue(originalObject);
                    var clonedFieldValue = InternalCopy(originalFieldValue, visited);
                    fieldInfo.SetValue(cloneObject, clonedFieldValue);
                }
            }
            public static T Copy<T>(this T original)
            {
                return (T)Copy((object)original);
            }
        }
    
        public class ReferenceEqualityComparer : EqualityComparer<Object>
        {
            public override bool Equals(object x, object y)
            {
                return ReferenceEquals(x, y);
            }
            public override int GetHashCode(object obj)
            {
                return obj.GetHashCode();
            }
        }
    
        namespace ArrayExtensions
        {
            public static class ArrayExtensions
            {
                public static void ForEach(this Array array, Action<Array, int[]> action)
                {
                    if (array.LongLength == 0) return;
                    var walker = new ArrayTraverse(array);
                    do action(array, walker.Position);
                    while (walker.Step());
                }
            }
    
            internal class ArrayTraverse
            {
                public int[] Position;
                private readonly int[] _maxLengths;
    
                public ArrayTraverse(Array array)
                {
                    _maxLengths = new int[array.Rank];
                    for (var i = 0; i < array.Rank; ++i)
                    {
                        _maxLengths[i] = array.GetLength(i) - 1;
                    }
                    Position = new int[array.Rank];
                }
    
                public bool Step()
                {
                    for (var i = 0; i < Position.Length; ++i)
                    {
                        if (Position[i] >= _maxLengths[i]) continue;
                        Position[i]++;
                        for (var j = 0; j < i; j++)
                        {
                            Position[j] = 0;
                        }
                        return true;
                    }
                    return false;
                }
            }
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 11月16日
  • 已采纳回答 11月8日
  • 赞助了问题酬金20元 11月7日
  • 创建了问题 11月7日

悬赏问题

  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题