有没有更好的方法在C#中创建深克隆和浅克隆?我一直在为项目创建对象,在某些情况下,我必须创建这些对象的深层副本,我曾考虑过使用C#的内置函数MemberwiseClone()。困扰我的问题是,每当我创建一个新类时,我都必须编写一个像下面代码这样的函数来进行浅拷贝。有人请帮助我改进这部分并给我一个比第二行代码更好的浅拷贝。谢谢:)SHALLOWCOPY:publicstaticRoomTypeCreateTwin(RoomTyperoomType){return(roomType.MemberwiseClone()asRoomType);}DeepCopy:publicstaticTCreateDeepClone(Tsource){if(!typeof(T).IsSerializable){thrownewArgumentException("Thetypemustbeserializable.","source");}if(Object.ReferenceEquals(source,null)){returndefault(T);}IFormatter格式化程序=newBinaryFormatter();流stream=newMemoryStream();使用(流){formatter.Serialize(流,源);stream.Seek(0,SeekOrigin.Begin);返回(T)格式化程序。反序列化(流);}}MemberwiseClone不做深度复制(MSDN)一个不错的选择:MemberwiseClone方法通过创建一个新对象然后将当前对象的非静态字段复制到新对象来创建一个浅拷贝。如果该字段是值类型,则执行该字段的按位复制。如果该字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其克隆指的是同一个对象。这意味着如果克隆对象具有引用类型的公共字段或属性,它们将与原始对象的字段/属性一起提供到相同的内存位置,因此克隆对象中的每个更改都将反映在原始对象中。这不是真正的深拷贝。您可以使用BinarySerialization创建完全独立的对象实例,有关序列化示例,请参阅BinaryFormatter类的MSDN页面。示例和测试工具:用于创建给定对象的深层副本的扩展方法:publicstaticclassMemoryUtils{//////创建给定对象实例的深层副本//////给定对象的类型///要克隆的对象//////一个值,指示是否应该抛出异常///克隆时出错///返回给定对象的深层副本///使用BInarySerialization创建一个true深度复制publicstaticTObjectDeepCopy(thisTObjectinstance,boolthrowInCaseOfError)whereTObject:class{if(instance==null){thrownewArgumentNullException("instance");}TObjectclonedInstance=default(TObject);尝试{使用(varstream=newMemoryStream()){BinaryFormatterbinaryFormatter=newBinaryFormatter();binaryFormatter.Serialize(流,实例);//将位置重置为流的开头//反序列化将能够反序列化对象实例stream.Position=0;clonedInstance=(TObject)binaryFormatter.Deserialize(stream);}}赶上(Exceptionexception){stringerrorMessage=String.Format(CultureInfo.CurrentCulture,"ExceptionType:{0},Message:{1}{2}",exception.GetType(),exception.Message,exception.InnerException==null?String.Empty:String.Format(CultureInfo.CurrentCulture,"InnerException类型:{0},消息:{1}",exception.InnerException.GetType(),exception.InnerException.Message));Debug.WriteLine(errorMessage);如果(throwInCaseOfError){抛出;}}返回克隆实例;}}NUnit测试:publicclassMemoryUtilsFixture{[Test]publicvoidDeepCopyThrowWhenCopyInstanceOfNonSerializableType(){varnonSerializableInstance=newCustomNonSerializableType();Assert.Throws(()=>nonSerializableInstance.DeepCopy(true));}[测试]publicvoidDeepCopyThrowWhenPassedInNull(){objectinstance=null;Assert.Throws(()=>instance.DeepCopy(true));}[测试]publicvoidDeepCopyThrowWhenCopyInstanceOfNonSerializableTypeAndErrorsDisabled(){varnonSerializableInstance=新的CustomNonSerializableType();对象结果=空;Assert.DoesNotThrow(()=>result=nonSerializableInstance.DeepCopy(false));断言.IsNull(结果);}[测试]publicvoidDeepCopyShouldCreateExactAndIndependentCopyOfAnObject(){varinstance=newCustomSerializableType{DateTimeValueType=DateTime.Now.AddDays(1).AddMilliseconds(123).AddTicks(123),NumericValueType=777,StringValueType=Guid.NewGuid().ToString(),ReferenceType=newCustomSerializableType{DateTimeValueType=DateTime.Now,StringValueType=Guid.NewGuid().ToString()}};vardeepCopy=instance.DeepCopy(true);断言.IsNotNull(deepCopy);Assert.IsFalse(ReferenceEquals(instance,deepCopy));Assert.That(instance.NumericValueType==deepCopy.NumericValueType);Assert.That(instance.DateTimeValueType==deepCopy.DateTimeValueType);Assert.That(instance.StringValueType==deepCopy.StringValueType);Assert.IsNotNull(deepCopy.ReferenceType);断言.IsFalse(ReferenceEquals(instance.ReferenceType,deepCopy.ReferenceType));Assert.That(instance.ReferenceType.DateTimeValueType==deepCopy.ReferenceType.DateTimeValueType);Assert.That(instance.ReferenceType.StringValueType==deepCopy.ReferenceType.StringValueType);}[Serializable]内部密封类CustomSerializableType{publicintNumericValueType{get;放;}publicstringStringValueType{get;放;}publicDateTimeDateTimeValueType{get;放;}publicCustomSerializableTypeReferenceType{get;您还可以使用反射来创建对象的副本,这应该是最快的方法,因为序列化也使用反射这里有一些代码(测试过):publicstaticTDeepClone(thisToriginal,paramsObject[]args){returnoriginal.DeepClone(newDictionary(),args);}privatestaticTDeepClone(thisToriginal,Dictionarycopies,paramsObject[]args){Tresult;输入t=original.GetType();对象tmpResult;//检查对象是否已经被复制if(copies.TryGetValue(original,outtmpResult)){return(T)tmpResult;}else{if(!t.IsArray){/*创建新实例,此时你将参数传递给*构造函数如果没有默认构造函数*或者你将它更改为Activator.CreateInstance()如果有始终*默认构造函数*/result=(T)Activator.CreateInstance(t,args);副本。添加(原始,结果);//也许你需要更多的BindingFlagsforeach(FieldInfofieldint.GetFields(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.FlattenHierarchy|BindingFlags.Instance)){/*你可以在这里过滤字段(l查找属性并避免*不需要的字段)*/ObjectfieldValue=field.GetValue(original);//在这里检查实例是否应该被克隆Typeft=field.FieldType;/*您可以在此处检查ft.GetCustomAttributes(typeof(SerializableAttribute),false).Length!=0*避免不支持序列化的类型(例如NetworkStreams)*/if(fieldValue!=null&&!ft.IsValueType&&ft!=typeof(String)){fieldValue=fieldValue.DeepClone(copies);/*原生不支持子对象的参数,但您可以在使用*委托而不是Activator创建对象时提供它们。代表不应该在这里工作*他们需要更多的爱*/}field.SetValue(result,fieldValue);}}else{//在这里处理数组ArrayoriginalArray=(Array)(Object)original;数组resultArray=(Array)originalArray.Clone();副本。添加(原始,resultArray);//如果类型不是值类型,我们需要复制每个元素(!t.GetElementType().IsValueType){Int32[]长度=newInt32[t.GetArrayRank()];Int32[]indices=newInt32[lengths.Length];//从原始数组获取长度for(inti=0;i-1){indices[p]++;if(indicies[p]Update添加了一些代码,现在你可以使用这个方法来复制复杂的对象(甚至是多维数组)注意表示仍然没有实现。如果你想要一个完整的实现,你需要处理ISerializable接口,这不是很难,但需要一些时间来反映现有代码。这是一次远程实现吗?正如sll所建议的,使用序列化的解决方案是最简单的,但如果您尝试的类型不起作用克隆是不可序列化的。FelixK.的代码是一个很好的选择,但我发现它有一些问题。这是修复了我发现的一些问题的修订版本。我还删除了一些我认为的功能(例如构造函数参数)不需要。以上是C#学习教程:在C#中创建深浅克隆有没有更好的方法?分享的所有内容,如果对你有用,需要了解更多C#学习教程,希望大家多多关注—//////一种针对不可序列化类型的DeepClone方法。///publicstaticTDeepCloneWithoutSerialization(这个Toriginal){returnoriginal.deepClone(newDictionary());}staticTdeepClone(thisToriginal,Dictionarycopies){return(T)original.deepClone(typeof(T),copies);}/////在不使用序列化的情况下深度克隆一个对象。///创建对象(和递归)的每个字段的副本,以便我们最终得到///不包含对原始对象的任何引用的副本。//静态对象deepClone(thisobjectoriginal,Typet,Dictionarycopies){//检查对象是否不可变或更新时复制if(t.IsValueType||original==null||t==typeof(string)||t==typeof(Guid))returnoriginal;//接口对我们用处不大if(t.IsInterface)t=original.GetType();对象tmpResult;//检查对象是否已经被复制dif(copies.TryGetValue(original,outtmpResult))returntmpResult;对象结果;如果(!t.IsArray){结果=Activator.CreateInstance(t);副本。添加(原始,结果);//也许你需要更多的BindingFlagsforeachfield.SetValue(结果,fieldValue.deepClone(field.FieldType,副本));}}else{//在这里处理数组varoriginalArray=(Array)original;varresultArray=(Array)originalArray.Clone();副本。添加(原始,resultArray);varelementType=t.GetElementType();//如果类型不是值类型,我们需要复制每个元素if(!elementType.IsValueType){varlengths=newint[t.GetArrayRank()];varindicies=newint[长度.长度];//从原始数组获取长度for(vari=0;i-1){indicies[p]++;如果(指数[p]本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如有转载请注明出处:
