我需要制作一个MyGame类的副本,并在我选择移动游戏之前在我的模拟游戏试验中使用它。
例如:
public class MyGame
{
private int Start;
private Board board;
//Constructor
public void Play()
{
//play game
}
public object Clone()
{
}
}
public class Board
{
private int Count;
//Constructor
//Some methods and properties
public object Clone()
{
}
}
编写Clone()方法的代码我试过
我已经阅读了很多关于这个主题的文章和论坛。答案最多的人 使用是C#中的深度克隆对象,我尝试了关于我的项目的样本,但我仍然 让我的模拟修改原始对象(MyGame类)而不是副本。
答案 0 :(得分:4)
这里有一个深层复制的示例,它深度复制了与复制构造函数一起使用的所有引用类型对象:
public sealed class MyGame
{
private int start;
private Board board;
public MyGame(MyGame orig)
{
// value types - like integers - can easily be
// reused
this.start = orig.start;
// reference types must be clones seperately, you
// must not use orig.board directly here
this.board = new Board(orig.board);
}
}
public sealed class Board
{
private int count;
public Board(Board orig)
{
// here we have a value type again
this.count = orig.count;
// here we have no reference types. if we did
// we'd have to clone them too, as above
}
}
我认为您的副本可能以某种方式浅并重新使用一些引用(例如this.board = orig.board
而不是创建新板)。这是一个猜测,因为我看不到你的克隆实现。
此外,我使用复制构造函数而不是实现ICloneable
。实施几乎相同。但是,一个优点是您可以简化处理子类:
假设您有MyAwesomeGame : MyGame
,没有覆盖 MyGame.Clone
。你会从myAwesomeGame.Clone()
得到什么?实际上,仍然是一个新的MyGame
,因为MyGame.Clone
是负责的方法。然而,人们可能会不小心期望在这里正确克隆MyAwesomeGame
。 new MyGame(myAwesomeGame)
仍然不完整地复制,但更明显。在我的例子中,我创建了类sealed
来避免这种失败。如果你可以密封它们,那么它会有很好的改变,它会让你的生活变得更加简单。
一般不建议实施ICloneable
,有关详细信息和一般信息,请参阅Why should I implement ICloneable in c#?。
无论如何,我还是 ICloneable
方法,让事情变得完整,让您能够进行比较和对比:
public class MyGame : ICloneable
{
private int start;
private Board board;
public object Clone()
{
var copy = new MyGame();
copy.start = this.start;
copy.board = (Board)this.board.Clone();
return copy;
}
}
public class Board : ICloneable
{
private int count;
public object Clone()
{
var copy = new Board();
copy.count = this.count;
return copy;
}
}
答案 1 :(得分:1)
实现深度克隆的最简单,最可靠的方法是序列化,然后反序列化对象。这可能会产生很大的性能成本。考虑来自此命名空间的类以进行序列化http://msdn.microsoft.com/en-us/library/System.Xml.Serialization.aspx
深度克隆需要递归地创建每个属性的新实例,该属性不是值类型。克隆MyGame
需要MyGame
的新实例和Board
的新实例,这两个实例都填充了与原始值相同的Start
和Count
值。这是一个繁琐而又难以维持的噩梦。你可以猜到,它不是一个开箱即用的自动过程,但它可以是,使用反射(这是上面的xml序列化的工作方式。
MemberwiseClone
仅创建您调用它的对象的新实例 - 所有引用都保持不变。
答案 2 :(得分:1)
MemberwiseClone()
为对象的每个成员创建愚蠢的浅层克隆。当成员是值类型时,这可以正常工作,但是在引用类型的情况下,它会失败,因为它将克隆指针而不是指向对象。
从代码开始,成员克隆就是这样的:
public object Clone()
{
MyGame cloned = new MyGame();
cloned.Start = this.Start; // Copied (cloned) because value type
cloned.Board = this.Board; // This is not a copy, just a reference!
}
深度克隆的更好解决方案是为每个引用类型实现ICloneable
(例如,否则复制构造函数方法也很好),假设{{1}也是可复制的:
Board
请注意,在您的示例中,public object Clone()
{
MyGame cloned = new MyGame();
cloned.Start = this.Start;
cloned.Board = (Board)this.Board.Clone();
}
可以使用Board
实施Clone()
,因为其成员都是值类型。
如果你无法管理(例如因为代码不可访问)或者你需要一个快速/脏的解决方案,你可以考虑用户serializaiton(在内存中)。哪个序列化程序是一个很大的问题,每个都有一些限制(关于什么是序列化的以及如何)。例如,XML序列化程序不会序列化私有字段(它根本不会序列化字段)。更快的是二进制格式化程序,但您需要使用适当的属性标记每个类。
根据您喜欢的序列化程序进行更改(根据您的要求),在这种情况下,我假设您将MemberwiseClone()
和MyGame
标记为Board
以进行快速二进制序列化:
[Serializable]
答案 3 :(得分:0)
试试这个
public static T DeepCopy<T>(this T obj)
{
T result;
var serializer = new DataContractSerializer(typeof(T));
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
ms.Position = 0;
result = (T)serializer.ReadObject(ms);
ms.Close();
}
return result;
}
答案 4 :(得分:0)
我有两种扩展方法可用于实现此目的。下面的演示代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace SimpleCloneDemo
{
public class Program
{
public static void Main(string[] args)
{
var person = new Person { Id = 1, FirstName = "John", Surname = "Doe" };
var clone = person.Clone();
clone.Id = 5;
clone.FirstName = "Jane";
Console.WriteLine(@"person: {0}", person);
Console.WriteLine(@"clone: {0}", clone);
if (Debugger.IsAttached)
Console.ReadLine();
}
}
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public override string ToString()
{
return string.Format("Id: {0}, Full Name: {1}, {2}", Id, Surname, FirstName);
}
}
public static class ObjectExtensions
{
public static T Clone<T>(this T entity) where T : class
{
var clone = Activator.CreateInstance(entity.GetType());
var entityPropValueDictionary = entity.AsPropValueDictionary();
foreach (var prop in clone.GetType().GetProperties())
{
clone.GetType().GetProperty(prop.Name).SetValue(clone, entityPropValueDictionary[prop.Name]);
}
return clone as T;
}
public static IDictionary<string, object> AsPropValueDictionary<T>(this T instance, params BindingFlags[] bindingFlags)
{
var runtimeBindingFlags = BindingFlags.Default;
switch (bindingFlags.Count())
{
case 0:
runtimeBindingFlags = BindingFlags.Default;
break;
case 1:
runtimeBindingFlags = bindingFlags[0];
break;
default:
runtimeBindingFlags = bindingFlags.Aggregate(runtimeBindingFlags, (current, bindingFlag) => current | bindingFlag);
break;
}
return runtimeBindingFlags == BindingFlags.Default
? instance.GetType().GetProperties().ToDictionary(prop => prop.Name, prop => prop.GetValue(instance))
: instance.GetType().GetProperties(runtimeBindingFlags).ToDictionary(prop => prop.Name, prop => prop.GetValue(instance));
}
}
}
结果:
我匆忙编写了这些快速和脏的扩展方法,因此可能存在一些问题,它们可能非常低效,但它们似乎适用于我的用例。他们也可以帮助你。