我需要实现一个具有继承的C#深拷贝构造函数。有哪些模式可用?我希望在C#中实现类层次结构的深度克隆publicClassParentObj:ICloneable{protectedintmyA;publicvirtualObjectClone(){ParentObjnewObj=newParentObj();newObj.myA=theObj.MyA;返回新对象;}}publicClassChildObj:ParentObj{protectedintmyB;publicoverrideObjectClone(){ParentnewObj=this.base.Clone();newObj.myB=theObj.MyB;返回新对象;这是行不通的,因为克隆的孩子只有父母是新的。在我的代码中,一些类具有很大的层次结构。这样做的推荐方法是什么?在不调用基类的情况下克隆每个级别的所有内容似乎是错误的?这个问题一定有一些巧妙的解决办法,它们是什么?我可以谢谢大家的回答吗?看到一些方法真的很有趣。我认为如果有人给出一个完整的反思性答案的例子会很好。+1等待!典型的方法是使用C++中的“复制构造函数”模式:classBase:ICloneable{intx;受保护的基础(其他基础){x=other.x;}publicvirtualobjectClone(){returnnewBase(this);}}派生类:Base{inty;protectedDerived(Derivedother):Base(other){y=other.y;}publicoverrideobjectClone(){returnnewDerived(this);另一种方法是在您的Clone实现中使用Object.MemberwiseClone-这将确保结果始终是正确的类型并允许覆盖扩展:classBase:ICloneable{Listxs;publicvirtualobjectClone(){Baseresult=this.MemberwiseClone();//xs在这里指向同一个List对象,但是我们想要//一个带有数据结果副本的新List对象。xs=新列表(xs);返回结果;}}classDerived:Base{列表ys;publicoverrideobjectClone(){//Cast是合法的,因为MemberwiseClone()将使用对象的//实际类型来实例化副本。派生结果=(Derived)base。克隆();//ys在这里指向同一个List对象,但是我们想要//一个带有数据副本的新List对象结果.ys=新列表(ys);返回结果;}}这两种方法都要求层次结构中的所有类都遵循以下模式:使用哪个是偏好问题。如果您有任何随机类实现了ICloneable而没有实现保证(除了遵循ICloneable的文档语义),则无法扩展它。尝试序列化技巧:publicobjectClone(objecttoClone){BinaryFormatterbf=newBinaryFormatter();MemoryStreamms=newMemoryStream();bf.Serialize(ms,toClone);ms.Flush();ms.Position=0;返回bf.Deserialize(ms);}警告:使用此代码时应格外小心。使用风险自负。此示例按原样提供,不提供任何形式的保证。还有另一种方法可以在对象图上执行深度克隆。在考虑使用此示例时,请务必注意以下几点:缺点:任何对外部类的引用也将被克隆,除非将这些引用提供给Clone(object,...)方法。不会在克隆的对象上执行任何构造函数,它们将被完全复制。不会实现ISerializable或序列化构造函数。无法在特定类型上更改此方法的行为。它会克隆一切,Streams、AppDomains、Forms等,这会以可怕的方式破坏您的应用程序。它可能会中断,而使用序列化方法更有可能继续工作。下面的实现使用递归,如果对象图太深,很容易导致堆栈溢出。那你为什么要用它?优点:它对所有实例数据进行完整的深度复制,而无需在对象中进行编码。它保留了重构对象中的所有对象图引用(甚至是循环引用)。它的执行速度比二进制格式化程序快20倍以上,并且占用的内存更少。它不需要任何东西,不需要属性、实现的接口、公共属性,什么都不需要。代码用法:你只需用一个对象调用它:Class1copy=Clone(myClass1);或者假设您有一个子对象并且您订阅了它的事件……现在您想要克隆该子对象。通过提供未克隆的对象列表,您可以保留对象图的某些部分:Class1copy=Clone(myClass1,this);执行:现在让我们开始吧……这是入口点:foreach(objectoinstableReferences)graph.Add(o,o);返回InternalClone(输入,图形);现在这很简单,它只是在克隆期间为对象构建一个字典映射,并用任何不应该被克隆的对象填充它。你会注意到提供给字典的比较器是一个ReferenceComparer,让我们看看它做了什么:}intIEqualityComparer.GetHashCode(objectobj){returnRuntimeHelpers.GetHashCode(obj);这很简单,只是一个比较器,它使用System.Object的gethash和reference强制相等......现在是艰苦的工作:privatestaticTInternalClone(Tinput,Dictionarygraph){if(input==null||inputis字符串||input.GetType().IsPrimitive)返回输入;键入inputType=input.GetType();对象存在;if(graph.TryGetValue(input,outexists))return(T)exists;if(inputisArray){ArrayarItems=(Array)((Array)(object)input).Clone();graph.Add(输入,arItems);for(longix=0;ix你会注意到数组和委托副本的特殊情况。每个都有自己的原因,第一个数组没有可以克隆的“成员”,所以你必须处理它并依赖onshallowClone()成员然后克隆每个元素。至于委托,它可以在没有特殊情况的情况下工作;但是,这会更安全,因为它不会像RuntimeMethodHandle那样被复制。如果你打算运行核心在运行时(例如System.Type)包含层次结构中的其他东西,我建议您以类似的方式显式处理它们。最后一种情况,也是最常见的情况,只是使用与BinaryFormatter使用的例程大致相同的例程。这些允许我们从原始对象中弹出所有实例字段(公共或私有),克隆它们,并将它们粘贴到空对象中。这里的好处是GetUninitializedObject返回一个没有正在运行的ctor的新实例,这可能会导致问题并降低性能。以上是否有效将高度依赖于您的特定对象图及其中的数据。如果您控制图中的对象并且知道它们不引用诸如Thread之类的愚蠢的东西,那么上面的代码应该可以正常工作。测试:这是我写的最初测试这个:.Name);}),newAction(delegate(StringBuildersb){sb.AppendLine(this.Name);}));名字=名字;孩子=孩子;}公共字符串名称;公共测试[]儿童;公共行动打印;}staticvoidMain(string[]args){Dictionarydata2,data=newDictionary(StringComparer.OrdinalIgnoreCase);测试a、b、c;data.Add("a",a=newTest("a",newTest("aa")));a.Children[0].Children=newTest[]{a};data.Add("b",b=newTest("b",a));data.Add("c",c=newTest("c"));data2=克隆(数据);Assert.IsFalse(Object.ReferenceEquals(data,data2));//基本内容测试&比较Assert.IsTrue(data2.ContainsKey("a"));Assert.IsTrue(data2.ContainsKey("A"));Assert.IsTrue(data2.ContainsKey("B"));//data和data2的节点不同Assert.IsFalse(Object.ReferenceEquals(数据["a"],data2["a"]));Assert.IsFalse(Object.ReferenceEquals(data["a"].Children[0],data2["a"].Children[0]));Assert.IsFalse(Object.ReferenceEquals(data["B"],data2["B"]));Assert.IsFalse(Object.ReferenceEquals(data["B"].Children[0],data2["B"].Children[0]));Assert.IsFalse(Object.ReferenceEquals(data["B"].Children[0],data2["A"]));//图形内部引用仍然完好无损?断言.IsTrue(Object.ReferenceEquals(data["B"].Children[0],data["A"]));Assert.IsTrue(Object.ReferenceEquals(data2["B"].Children[0],data2["A"]));Assert.IsTrue(Object.ReferenceEquals(data["A"].Children[0].Children[0],data["A"]));Assert.IsTrue(Object.ReferenceEquals(data2["A"].Children[0].Children[0],data2["A"]));data2["A"].Name="重新";StringBuildersb=newStringBuilder();data2["A"].Print(sb);Assert.AreEqual("anewrnanewrn",sb.ToString());最后一点:老实说,当深度克隆数据模型通常是一件好事时,这是一项有趣的运动。今天的现实是,大多数生成的数据模型将使用生成的深度克隆例程来避免上述黑客攻击的用处。我强烈建议您生成数据模型并能够执行深度克隆,而不是使用上面的代码。最好的方法是序列化您的对象,然后返回反序列化的副本。除了标记为不可序列化的对象外,它将获取有关该对象的所有内容,并使继承序列化变得容易。[可序列化]publicclassParentObj:ICloneable{privateintmyA;[非序列化]私有对象somethingInternal;publicvirtualobjectClone(){MemoryStreamms=newMemoryStream();BinaryFormatter格式化程序=newBinaryFormatter();格式化程序。序列化();对象克隆=格式化程序。反序列化(毫秒);返回克隆;}}[可序列化]publicclassChildObj:ParentObj{privateintmyB;//无需覆盖克隆,因为它仍将序列化当前对象,包括新的myB字段}这不是最有效的方法,但也不是替代方法:重新选择。这个选项的好处是它可以无缝继承。您可以使用反射来遍历所有变量并复制它们。(慢)如果您的软件运行缓慢,您可以使用DynamicMethod并生成il。序列化对象并再次反序列化。我认为您在这里没有正确实施ICloneable;它需要一个不带参数的Clone()方法。我的建议是:publicclassParentObj:ICloneable{publicvirtualObjectClone(){varobj=newParentObj();复制对象(这个,对象);}protectedvirtualCopyObject(ParentObjsource,ParentObjdest){dest.myA=source.myA;}}publicclassChildObj:ParentObj{publicoverrideObjectClone(){varobj=newChildObj();复制对象(这个,对象);}publicoverrideCopyObject(ChildObjsource,ParentObjdest){base.CopyObject(source,dest)dest.myB=source.myB;请注意,CopyObject()基本上是Object.MemberwiseClone(),假设您不只是复制值,您还克隆任何类的成员。尝试以下[使用关键字“new”]publicclassParent{privateint_X;publicintX{set{_X=value;}get{return_X;}}publicParentcopy(){returnnewParent{X=this.X};}}publicclassChild:Parent{privateint_Y;publicintY{set{_Y=value;}get{return_Y;}}publicnewChildcopy(){returnnewChild{X=this.X,Y=this.Y};你应该使用MemberwiseClone方法:以上是C#学习教程:我需要用继承实现C#深拷贝构造函数。有哪些模式可用?如果分享的内容对你有用,需要了解更多C#学习教程,希望你多多关注——publicclassParentObj:ICloneable{protectedintmyA;publicvirtualObjectClone(){ParentObjnewObj=this.MemberwiseClone()asParentObj;newObj.myA=this.MyA;//不需要,因为值类型(int)已经自动复制。返回新对象;}}publicclassChildObj:ParentObj{protectedintmyB;publicoverrideObjectClone(){ChildObjnewObj=base.Clone()asChildObj;newObj.myB=this.MyB;//不需要,因为值类型(int)已经自动复制returnnewObj;}}本文收集自网络,不代表立场。侵权请点击右侧联系管理员删除。如需转载请注明出处:
