结构化或深度复制-C#
本文关键字:-C# 复制 深度 结构化 | 更新日期: 2023-09-27 17:58:43
我终于成功地复制了对象的值类型,一个使用字典存储动态属性的类。不过,我想知道两件事,单声道兼容性和效率。我是C#的新手,对编程还有很多需要学习的地方,所以如果我误用了几个短语,我很抱歉:P
我使用了这个方法。如何在.NET(特别是C#)中对对象进行深度复制。。。复制我的对象。我也会有数百个这样的对象,我想知道以这种方式复制它们是非常低效的吗?structs会是更好的选择吗?然而,我不确定什么时候该使用structs。可以用单声道移植吗?一些谷歌搜索表明,这种连载可能会引发一个问题。
根据评论中的后续响应,您所寻找的最佳解决方案是最简单的:自己编写代码来复制对象。
如果你的类真的很简单,就像一个为自定义属性存储键/值对的字典的包装器一样,你可以使用dictionary(IDictionary)构造函数将值从一个字典复制到另一个字典。
class MyWrapper
{
private Dictionary<string, object> properties =
new Dictionary<string, object>();
public MyWrapper Clone()
{
MyWrapper output = new MyWrapper();
output.properties = new Dictionary<string, object>(properties);
}
}
显然,这是一个简化的类,实际上并没有做任何事情,但从您所描述的内容来看,这应该会得到您所需要的。没有反射,没有"gotchas",只是一个简单的值从一个字典复制到另一个字典。
编辑
我不能谈论单声道可移植性,因为我是一名仅限Windows的开发人员,但就效率而言,复制所需内容的显式解决方案将是基于反射的解决方案的赢家。
在基于引用的面向对象语言中,任何任意类型的真正深度复制的概念都不是容易(甚至安全)实现的。虽然简单的类复制起来很琐碎,但引用循环、没有无参数构造函数的类和不可变类型带来了挑战。
例如,考虑这个类:
public class Foo
{
public Foo Next { get; set; }
}
这几乎是单链表的最简单实现。天真的深度复制算法将从Foo
的第一个实例开始,然后通过递归地向下导航Next
引用链来克隆它,直到它遇到null
值。然而,这样做,我们不仅会消耗内存,而且最终会得到不代表原始复制品的对象:
Foo first = new Foo();
first.Next = new Foo();
first.Next.Next = first;
这是一件完全合法(甚至合理)的事情,但现在我们有了一个循环引用循环,它会破坏我们天真的克隆算法。所以现在我们必须实现一个对象缓存。
Dictionary<object, object> clonedObjects;
现在,在克隆算法中,当为属性或字段分配值时,我们会检查缓存,看看我们要复制的引用是否已经被克隆。如果有,我们就使用这个值,而不是克隆一个新的值。这将给我们一个全新的对象图,它代表我们的原始对象,也是一个完整的克隆。很棒,对吧?
那么,无参数构造函数呢?这个问题甚至不能完全从一般意义上解决。如果我创建这个类:
public class Bar
{
public Bar(string gotcha) { }
}
没有办法在天真的意义上克隆这个类;由于您无法知道如何调用构造函数(您可以反射式地获得ConstructorInfo
,但如何调用它的语义将完全未知。您可以做的最好的事情是存储元数据(通过类上的自定义属性),说明要调用哪个构造函数以及如何调用它(例如,按传递顺序排列的字段列表),但这需要先前对克隆机制的了解,也意味着构造函数的参数是原始对象上的字段,但事实并非如此。
现在,我们在混合中抛出了另一个障碍:不可变引用类型。这种情况也可能导致意外行为。不可变引用类型是指其(外部可见)值不能更改的引用类型;在大多数情况下,这些类被设计为展示值类型语义。他们也经常缺乏无参数构造函数(甚至可能根本没有可公开访问的构造函数),这让他们受到了我们之前的愤怒,但他们也可能使用基于工厂的方法,这样他们就可以确保引用相等也意味着值相等,反之亦然(后一种情况不太常见,但如果我们谈论的算法是一种完全幼稚的克隆机制,那么我们必须涵盖它)。同样,这意味着另一个自定义属性,指示克隆机制应该简单地复制引用,而不是克隆实际对象。
因此,简而言之,在处理任意类型时,完全天真的深度复制机制是不可能的。类型在设计时必须考虑到克隆机制,并且可能必须做出让步或以特定的方式装饰自己才能使用它。这一点,再加上相对罕见的需求,可能就是为什么现在没有框架级的深度复制机制的原因,以及为什么你应该考虑一个更明确的复制机制,让你知道复制什么(以及什么可能无关紧要),这样你就可以确定你得到的是你想要的。
结构有一些独特的特性,在使用它们之前应该仔细考虑。
- 它们都是值类型(这将为通过考试增加挑战周围)
- 它们都占用堆栈上的内存而不是堆
- 它们必须具有无参数构造函数
- 结构没有继承
http://msdn.microsoft.com/en-us/library/aa288471(v=vs.71).aspx
在询问structs是否适合您的情况之前,请考虑这些要点。
我通常会坚持只为我的类编写一个方法来创建一个深度副本。您可能会非常喜欢并使用泛型和反射的组合来编写一个非常抽象的DeepClone<T>(T object)
类型的方法。或者走一条简单的路,只写一个适合有问题的班级的裁缝。