在C#中,向类添加(深层)复制功能的首选方法是什么?是应该实现复制构造函数,还是从派生ICloneable并实现该Clone()方法?
备注:我在括号内写了"深刻",因为我认为这是无关紧要的.显然其他人不同意,所以我问复制构造函数/操作符/函数是否需要明确它实现的副本变体.
Sim*_*ens 84
你不应该来自ICloneable.
原因是,当Microsoft设计.net框架时,他们从未指定Clone()方法是ICloneable应该是深度还是浅度克隆,因此接口在语义上会被破坏,因为调用者不会知道调用是深入还是浅层克隆对象.
相反,您应该使用(和)方法定义自己的IDeepCloneable(和IShallowCloneable)接口.DeepClone()ShallowClone()
您可以定义两个接口,一个用于支持强类型克隆的泛型参数,另一个用于在处理不同类型的可克隆对象的集合时不保留弱类型克隆功能:
public interface IDeepCloneable
{
object DeepClone();
}
public interface IDeepCloneable<T> : IDeepCloneable
{
T DeepClone();
}
Run Code Online (Sandbox Code Playgroud)
然后你会像这样实现:
public class SampleClass : IDeepCloneable<SampleClass>
{
public SampleClass DeepClone()
{
// Deep clone your object
return ...;
}
object IDeepCloneable.DeepClone()
{
return this.DeepClone();
}
}
Run Code Online (Sandbox Code Playgroud)
一般来说,我更喜欢使用描述的接口而不是复制构造函数,它使目的非常清晰.复制构造函数可能会被假定为深度克隆,但它肯定不像使用IDeepClonable接口那样明确.
这在.net 框架设计指南和Brad Abrams博客中进行了讨论
(我想如果你正在编写一个应用程序(而不是框架/库),那么你可以确定你团队之外没有人会调用你的代码,这并不重要,你可以分配一个语义含义对.net ICloneable接口进行"深度克隆",但是你应该确保在你的团队中有很好的文档记录并且很好理解.我个人也坚持框架指南.)
Jef*_*dge 31
在C#中,向类添加(深层)复制功能的首选方法是什么?是应该实现复制构造函数,还是从ICloneable派生并实现Clone()方法?
这个问题ICloneable是,正如其他人所提到的,它并没有指定它是一个深或浅拷贝,这使得它几乎unuseable,并在实践中,很少使用.它也会返回object,这是一种痛苦,因为它需要大量的铸造.(虽然你特意在问题中提到的类,实施ICloneable上struct需要拳击.)
复制构造器也遇到ICloneable的一个问题.复制构造函数是执行深层还是浅层复制并不明显.
Account clonedAccount = new Account(currentAccount); // Deep or shallow?
Run Code Online (Sandbox Code Playgroud)
最好创建一个DeepClone()方法.这样的意图非常明确.
这提出了一个问题,即它应该是静态方法还是实例方法.
Account clonedAccount = currentAccount.DeepClone(); // instance method
Run Code Online (Sandbox Code Playgroud)
要么
Account clonedAccount = Account.DeepClone(currentAccount); // static method
Run Code Online (Sandbox Code Playgroud)
我有时稍微喜欢静态版本,只是因为克隆似乎是对象所做的事情,而不是对象正在做的事情.在任何一种情况下,克隆作为继承层次结构一部分的对象时都会遇到问题,以及这些问题如何交叉可能最终推动设计.
class CheckingAccount : Account
{
CheckAuthorizationScheme checkAuthorizationScheme;
public override Account DeepClone()
{
CheckingAccount clone = new CheckingAccount();
DeepCloneFields(clone);
return clone;
}
protected override void DeepCloneFields(Account clone)
{
base.DeepCloneFields(clone);
((CheckingAccount)clone).checkAuthorizationScheme = this.checkAuthorizationScheme.DeepClone();
}
}
Run Code Online (Sandbox Code Playgroud)
Dav*_*eau 21
我建议在克隆方法上使用复制构造函数主要是因为克隆方法会阻止您创建readonly可能已经使用构造函数的字段.
如果需要多态克隆,则可以向基类中添加一个abstract或virtual Clone()方法,通过调用复制构造函数来实现.
如果您需要多种副本(例如:深/浅),您可以在复制构造函数中使用参数指定它,尽管根据我的经验,我发现通常深度和浅层复制的混合是我需要的.
例如:
public class BaseType {
readonly int mBaseField;
public BaseType(BaseType pSource) =>
mBaseField = pSource.mBaseField;
public virtual BaseType Clone() =>
new BaseType(this);
}
public class SubType : BaseType {
readonly int mSubField;
public SubType(SubType pSource)
: base(pSource) =>
mSubField = pSource.mSubField;
public override BaseType Clone() =>
new SubType(this);
}
Run Code Online (Sandbox Code Playgroud)
Dan*_*anO 17
有一个很好的论据,你应该使用受保护的拷贝构造函数实现clone()
最好提供受保护(非公共)的复制构造函数,并从克隆方法中调用它.这使我们能够将创建对象的任务委托给类本身的实例,从而提供可扩展性,并使用受保护的拷贝构造函数安全地创建对象.
所以这不是一个"对抗"的问题.您可能需要复制构造函数和克隆接口才能正确执行.
(尽管推荐的公共接口是Clone()接口而不是基于Constructor的接口.)
不要在其他答案中明确深入或浅薄的论点中被抓住.在现实世界中,它几乎总是介于两者之间 - 无论哪种方式,都不应该是呼叫者的关注点.
克隆()合约只是"当我改变第一个时不会改变".您需要复制多少图形,或者如何避免无限递归才能实现这一点,这与调用者无关.
Gra*_*ton 12
实现ICloneable是不推荐的,因为没有指定它是深拷贝还是浅拷贝,所以我会去构造函数,或者只是自己实现一些东西.也许称之为DeepCopy()让它变得非常明显!
Mat*_*sen 11
您将遇到复制构造函数和抽象类的问题.想象一下,你想要做以下事情:
abstract class A
{
public A()
{
}
public A(A ToCopy)
{
X = ToCopy.X;
}
public int X;
}
class B : A
{
public B()
{
}
public B(B ToCopy) : base(ToCopy)
{
Y = ToCopy.Y;
}
public int Y;
}
class C : A
{
public C()
{
}
public C(C ToCopy)
: base(ToCopy)
{
Z = ToCopy.Z;
}
public int Z;
}
class Program
{
static void Main(string[] args)
{
List<A> list = new List<A>();
B b = new B();
b.X = 1;
b.Y = 2;
list.Add(b);
C c = new C();
c.X = 3;
c.Z = 4;
list.Add(c);
List<A> cloneList = new List<A>();
//Won't work
//foreach (A a in list)
// cloneList.Add(new A(a)); //Not this time batman!
//Works, but is nasty for anything less contrived than this example.
foreach (A a in list)
{
if(a is B)
cloneList.Add(new B((B)a));
if (a is C)
cloneList.Add(new C((C)a));
}
}
}
Run Code Online (Sandbox Code Playgroud)
在完成上述操作之后,您开始希望您使用接口,或者为DeepCopy()/ ICloneable.Clone()实现.
| 归档时间: |
|
| 查看次数: |
69013 次 |
| 最近记录: |