复制对象并使用“复制”而不更改“原始”
本文关键字:复制 原始 对象 | 更新日期: 2023-09-27 18:24:08
假设我有一个Object ItemVO,其中已经分配了一堆属性。例如:
ItemVO originalItemVO = new ItemVO();
originalItemVO.ItemId = 1;
originalItemVO.ItemCategory = "ORIGINAL";
我想创建另一个重复使用:
duplicateItemVO = originalItemVO;
然后使用duplicateItemVO并更改其属性,而不更改原始ItemVO:
// This also change the originalItemVO.ItemCategory which I do not want.
duplicateItemVO.ItemCategory = "DUPLICATE"
如何在不更改类ItemVO的情况下实现这一点?
感谢
public class ItemVO
{
public ItemVO()
{
ItemId = "";
ItemCategory = "";
}
public string ItemId { get; set; }
public string ItemCategory { get; set; }
}
为了在不更改另一个实例的情况下更改一个实例,您需要克隆此实例的实际值,而不是引用。.Net中使用的模式是实现ICloneable。所以你的代码看起来是这样的:
public class ItemVO: ICloneable
{
public ItemVO()
{
ItemId = "";
ItemCategory = "";
}
public string ItemId { get; set; }
public string ItemCategory { get; set; }
public object Clone()
{
return new ItemVO
{
ItemId = this.ItemId,
ItemCategory = this.ItemCategory
};
}
}
现在请注意,在使用Clone()时需要显式强制转换(或者您可以自己生成返回ItemVO的强制转换)。
duplicateItemVO = (ItemVO) originalItemVO.Clone();
要按值而不是引用复制对象,可以序列化它(例如JSON),然后立即反序列化它。然后您就有了一个按值复制的副本。
这里有一个的例子
ItemVO originalItemVO = new ItemVO();
originalItemVO.ItemId = 1;
originalItemVO.ItemCategory = "ORIGINAL";
string json = Newtonsoft.Json.JsonConvert.SerializeObject(originalItemVO);
ItemVO duplicateItemVO = Newtonsoft.Json.JsonConvert.DeserializeObject<ItemVO>(json);
您需要构建类的新实例,而不仅仅是分配变量:
duplicateItemVO = new ItemVO
{
ItemId = originalItemVO.ItemId,
ItemCategory = originalItemVO.ItemCategory
};
当您处理引用类型(任何类)时,只分配一个变量就是创建对原始对象的引用的副本。因此,在该对象中设置属性值也会更改原始对象。为了防止这种情况,您需要实际构建一个新的对象实例。
实际上无法复制对象,因为它们更有可能是引用类型。理想的方法是将对象序列化或流式传输到新对象中——前提是类是可序列化的(通过在类声明中提供[serializable]属性)。
private static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
现在你可以使用这个代码:
[Serializable]
public class MyClass
{
public int a {get; set;}
public int b {get; set;}
}
var obj = new MyClass{
a = 10,
b = 20,
};
var newobj = Clone<MyClass>(obj);
你将得到一个全新的obj副本。注意:MyClass中的任何其他类也必须使用特性[Serializable]声明。
我建议在下面的链接中按原样使用。对我来说效果很好。
https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone(v=vs.110).aspx
public Person ShallowCopy ()
{
return (Person) this.MemberwiseClone ();
}
类是引用类型,当您更改一个实例时,它将更改原始引用。因此,使用值类型对象来克服您的任务(例如:使用结构而不是类)
public struct ItemVO { *** }
或者您可以为类实现ICloneable接口
默认情况下,对象是引用类型。
将一个对象分配给另一个对象意味着您只需引用对象的地址。任何对象中的任何更改都将反映在两者中。
要解决这个问题,您应该使用"new"关键字初始化对象,然后在第一个对象中添加此对象值。
从c#4.5开始,基类对象包含一个名为MemberwiseClone的方法,该方法使您能够执行该对象的浅层复制,并将结果作为新实例返回。(如果字段是值类型,则执行字段的逐位复制。如果字段是引用类型,则复制引用,但引用的对象不是;因此,原始对象及其克隆引用同一对象。)
如果您希望实现原型设计模式,这将非常有用。
如果您希望实现深度复制(类中的所有内容都作为新实例复制),那么序列化或反射可能是最好的工具