我想做一些事情,比如:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
我想做一些事情,比如:
MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();
然后对未反映在原始对象中的新对象进行更改。
我通常不需要这个功能,所以当有必要时,我会创建一个新对象,然后单独复制每个属性,但这总是让我觉得有更好或更优雅的方法来处理这种情况。
如何克隆或深度复制对象,以便在原始对象中不反映任何更改的情况下修改克隆的对象?
当前回答
除了这里的一些精彩答案外,您可以在C#9.0及更高版本中完成以下操作(假设您可以将类转换为记录):
record Record
{
public int Property1 { get; set; }
public string Property2 { get; set; }
}
然后,只需使用with运算符将一个对象的值复制到新对象。
var object1 = new Record()
{
Property1 = 1,
Property2 = "2"
};
var object2 = object1 with { };
// object2 now has Property1 = 1 & Property2 = "2"
我希望这有助于:)
其他回答
如果使用net.core并且对象是可序列化的,则可以使用
var jsonBin = BinaryData.FromObjectAsJson(yourObject);
then
var yourObjectCloned = jsonBin.ToObjectFromJson<YourType>();
BinaryData在dotnet中,因此您不需要第三方库。它还可以处理类上的属性为Object类型的情况(属性中的实际数据仍然需要可序列化)
C#9.0引入了需要记录的with关键字(谢谢Mark Nading)。这应该允许非常简单的对象克隆(如果需要,还可以进行突变),只需要很少的样板,但只需要一个记录。
您似乎无法通过将类放入通用记录来克隆(按值)类;
using System;
public class Program
{
public class Example
{
public string A { get; set; }
}
public record ClonerRecord<T>(T a)
{
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = (new ClonerRecord<Example>(foo) with {}).a;
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
这写着“再见世界:()”-字符串是通过引用复制的(不需要)。https://dotnetfiddle.net/w3IJgG
(令人难以置信的是,上面的方法可以正确地用于结构!https://dotnetfiddle.net/469NJv)
但克隆一条记录似乎确实可以按值进行缩进和克隆。
using System;
public class Program
{
public record Example
{
public string A { get; set; }
}
public static void Main()
{
var foo = new Example {A = "Hello World"};
var bar = foo with {};
foo.A = "Goodbye World :(";
Console.WriteLine(bar.A);
}
}
这将返回“Hello World”,字符串是按值复制的!https://dotnetfiddle.net/MCHGEL
更多信息可以在博客文章中找到:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression
在我使用的代码库中,我们有一个来自GitHub项目Burtsev Alexey/net对象深度副本的ObjectExtension.cs文件副本。它已经9岁了。虽然我们后来意识到,对于更大的对象结构来说,它是非常慢的,但它还是有效的。
相反,我们在GitHub项目jpmikkers/Baksteen.Extensions.DeepCopy中找到了ObjectExtension.cs文件的一个分支。以前对大型数据结构的深度复制操作需要大约30分钟,现在感觉几乎是瞬间完成的。
此改进版本包含以下文档:
用于快速对象克隆的C#扩展方法。这是Alexey Burtsev深度复印机的速度优化叉。根据您的使用情况,这将比原始版本快2倍-3倍。它还修复了原始代码中存在的一些错误。与经典的二进制序列化/反序列化深度克隆技术相比,此版本的速度大约快七倍(对象包含的数组越多,加速因子越大)。通过以下技术实现加速:缓存对象反射结果不要深度复制原语或不可变结构&类(例如枚举和字符串)为了提高引用的局部性,处理内部循环中的“快速”维度或多维数组使用编译的lambda表达式调用MemberwiseClone如何使用:使用Baksteen.Extensions.DeepCopy;...var myobject=新建SomeClass();...var myclone=myobject.DepCopy()!;//创建原始对象的新深度副本注意:只有在项目中启用了可为null的引用类型时,才需要感叹号(null原谅运算符)
我在Silverlight中使用ICloneable时遇到了问题,但我喜欢序列化的想法,我可以序列化XML,所以我这样做了:
static public class SerializeHelper
{
//Michael White, Holly Springs Consulting, 2009
//michael@hollyspringsconsulting.com
public static T DeserializeXML<T>(string xmlData)
where T:new()
{
if (string.IsNullOrEmpty(xmlData))
return default(T);
TextReader tr = new StringReader(xmlData);
T DocItms = new T();
XmlSerializer xms = new XmlSerializer(DocItms.GetType());
DocItms = (T)xms.Deserialize(tr);
return DocItms == null ? default(T) : DocItms;
}
public static string SeralizeObjectToXML<T>(T xmlObject)
{
StringBuilder sbTR = new StringBuilder();
XmlSerializer xmsTR = new XmlSerializer(xmlObject.GetType());
XmlWriterSettings xwsTR = new XmlWriterSettings();
XmlWriter xmwTR = XmlWriter.Create(sbTR, xwsTR);
xmsTR.Serialize(xmwTR,xmlObject);
return sbTR.ToString();
}
public static T CloneObject<T>(T objClone)
where T:new()
{
string GetString = SerializeHelper.SeralizeObjectToXML<T>(objClone);
return SerializeHelper.DeserializeXML<T>(GetString);
}
}
创建扩展:
public static T Clone<T>(this T theObject)
{
string jsonData = JsonConvert.SerializeObject(theObject);
return JsonConvert.DeserializeObject<T>(jsonData);
}
这样称呼:
NewObject = OldObject.Clone();