首先,我们可能都同意最好的方法是在自定义对象/实体中实现复制功能。但请考虑这种情况。我们没有这个选项,我们不想编写能够制作实体精确副本的特定函数,因为将来会更改实体,因此我们的复制函数会失败。
以下是当前实体的简化版本:
[Serializable]
class MyEntity
{
public MyEntity()
{
}
public MyEntity(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public MyEntity Copy()
{
throw new NotImplementedException();
}
}
为了满足上述所有要求,我想出了两个解决方案:
//original...
MyEntity original = new MyEntity() { Id = 1, Name = "demo1" };
//first way to copy object...
List<MyEntity> list = new List<MyEntity>() { original};
MyEntity copy1 = list.ConvertAll(entity => new MyEntity(entity.Id, entity.Name))[0];
//second way to copy object...
byte[] bytes = SerializeEntity(original);
MyEntity copy2 = (MyEntity)DeserializeData(bytes);
byte[] SerializeEntity(object data)
{
byte[] result = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, data);
result = ms.ToArray();
}
return result;
}
object DeserializeData(byte[] data)
{
object result = null;
using(MemoryStream ms = new MemoryStream(data))
{
BinaryFormatter formatter = new BinaryFormatter();
result = formatter.Deserialize(ms);
}
return result;
}
现在问题。什么解决方案是幕后最优秀的,为什么,第一或第二?考虑到上述要求,有没有更好的方法来进行精确复制?副本将大量完成。
PS注意: 我知道第一种方式基本上已经是Honza指出的复制功能。我有点像序列化一样多功能,而且接近快速自定义复制功能。
答案 0 :(得分:7)
首先,我们可能都同意最好的方法是在自定义对象/实体中实现复制功能。
我不同意。我不想每次都写这样的方法。以下是我使用扩展方法的建议:
public static T Copy<T>(this T obj)
where T : class
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream) as T;
}
}
这基本上是您的第二个解决方案,但稍作优化。无需将MemoryStream复制到字节数组,然后从中创建另一个MemoryStream。
最好的是它是通用的,可以与具有[Serializable]
属性的每个对象一起使用。而且我很确定它比你必须访问每个属性的第一个解决方案更快(尽管我没有测量)。
修改强>
好的,我现在实际做了一些测量。我对表演的第一个假设是完全错误的!
我使用随机值创建了1000000个MyEntity对象然后复制它们(我还考虑了Honza Brestan
在深层和浅层副本上的提示):
使用二进制格式化程序进行深层复制:14.727 s
复制方法深拷贝:0.490 s
带反射的浅拷贝:5.499 s
复制方法浅拷贝:0.144秒
答案 1 :(得分:1)
第一次尝试和编写自己的复制方法有什么区别?
public MyEntity Copy()
{
return new MyEntity(this.Id, this.Name);
}
对我而言,这看起来比你的收集尝试要好,无论如何都要完全相同 - 在这两种情况下你都必须明确地命名所有的属性。
如果你不能修改实体类本身,你仍然可以创建一个扩展方法(放在一个静态类中,从你想要使用复制逻辑的位置可见)
public static MyEntity Copy(this MyEntity source)
{
return new MyEntity(source.Id, source.Name);
}
至于第二次尝试,您是否考虑过两者之间的差异?他们不太一样。第一个创建浅副本,而第二个(提供整个对象树可序列化)产生深副本。不同之处在于它的属性是否也被复制,或者原始对象及其副本都引用相同的对象。这同样适用于pescolino的版本,看起来非常好BTW。
所以问题是你想要/需要哪个副本。
对于一个真正动态(但可能不太有效)的浅复制方法,我认为你需要使用反射,枚举所有属性并将它们的值从原始对象复制到副本。非完整的演示版可能如下所示:
public static MyEntity Copy(this MyEntity source)
{
var result = new MyEntity();
var properties = source.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
var val = property.GetValue(source, null);
property.SetValue(result, val, null);
}
return result;
}
这种方法存在自身的问题,即性能,偶尔需要处理特殊情况(索引器,非公共属性......),但是会完成工作并且还可以处理不可序列化的对象。通用版本也很容易 - 这取决于你,无论你是否需要它。
另外值得注意的是因为我和pescolino都建议使用扩展方法,所以它们可能存在问题。如果您的实体确实包含与扩展名具有相同签名的Copy
方法,则编译器将决定使用它而不是扩展名。这显然会在被叫时抛出NotImplementedException
。因此,如果是这种情况(并且它不仅仅是您的示例代码),那么它可能是一个严重的“问题”。在这种情况下,唯一的解决方案是更改扩展方法的签名,最好通过更改其名称。
答案 2 :(得分:1)
您可以尝试使用AutoMapper:
Mapper.CreateMap<MyEntity, MyEntity>();
...
var copy3 = Mapper.Map<MyEntity, MyEntity>(original);