ICloneable
派生并实现Clone()
方法?
备注: 我在括号中写了“deep”,因为我认为这是不相关的。显然,其他人持不同意见,所以我问了一个问题:是否需要明确复制构造函数/运算符/函数实现哪个复制变体。ICloneable
派生并实现Clone()
方法?
备注: 我在括号中写了“deep”,因为我认为这是不相关的。显然,其他人持不同意见,所以我问了一个问题:是否需要明确复制构造函数/运算符/函数实现哪个复制变体。你不应该从ICloneable
派生。
原因是当微软设计.NET框架时,他们从未指定ICloneable
上的Clone()
方法应该是深克隆还是浅克隆,因此接口在语义上是有问题的,因为调用者不知道调用是否会深度或浅度克隆对象。
相反,你应该定义自己的IDeepCloneable
(和IShallowCloneable
)接口,并使用DeepClone()
(和ShallowClone()
)方法。
你可以定义两个接口,一个带有泛型参数以支持强类型克隆,另一个没有泛型参数以保留弱类型克隆能力,当你处理不同类型的可克隆对象集合时使用:
public interface IDeepCloneable
{
object DeepClone();
}
public interface IDeepCloneable<T> : IDeepCloneable
{
T DeepClone();
}
然后您可以像这样实现:
public class SampleClass : IDeepCloneable<SampleClass>
{
public SampleClass DeepClone()
{
// Deep clone your object
return ...;
}
object IDeepCloneable.DeepClone()
{
return this.DeepClone();
}
}
Account clonedAccount = new Account(currentAccount); // Deep or shallow?
最好创建一个DeepClone()方法。这样意图就非常清晰。
这引出了一个问题,它应该是静态方法还是实例方法。
Account clonedAccount = currentAccount.DeepClone(); // instance method
或者Account clonedAccount = Account.DeepClone(currentAccount); // static method
有时我稍微更喜欢使用静态版本,只是因为克隆似乎是针对一个对象进行的操作,而不是对象本身在执行的操作。无论哪种情况,当克隆属于继承层次结构的对象时,都需要处理问题,如何处理这些问题可能最终会影响设计。
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();
}
}
我建议使用复制构造函数而不是克隆方法,主要是因为如果您使用构造函数,可以将某些字段设置为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);
}
有一个很好的论点,即您应该使用受保护的复制构造函数implement clone() using a protected copy constructor
最好提供一个受保护(非公开)的复制构造函数,并从克隆方法调用它。这使我们能够将创建对象的任务委托给类本身的实例,从而提供可扩展性,并使用受保护的复制构造函数安全地创建对象。
因此,这不是一个“对立”的问题。您可能需要使用复制构造函数和克隆接口来正确执行。
(尽管推荐的公共接口是基于克隆的接口而不是基于构造函数的接口。)
不要陷入其他答案中的明确深层或浅层参数的争论中。在现实世界中,它几乎总是介于两者之间 - 无论如何,都不应该是调用者关心的问题。
Clone()合同只是“当我更改第一个时不会更改”。您必须复制多少图形,或者如何避免无限递归以使其发生,都不应该让调用者担心。
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));
}
}
}
在执行上述操作后,您会开始希望要么使用接口,要么使用DeepCopy()/ICloneable.Clone()实现。
不建议实现ICloneable接口,因为它没有指定是深拷贝还是浅拷贝,所以我建议使用构造函数或自己实现一个方法。也许可以将其命名为DeepCopy()以使其更加明显!
DeepClone()
方法的IDeepCloneable<T>
接口来实现深度克隆。 - Kent Boogaartpublic object DeepCopy(object source)
{
// Copy with Binary Serialization if the object supports it
// If not try copying with XML Serialization
// If not try copying with Data contract Serailizer, etc
}
ICloneable的问题在于意图和一致性。它从未清楚地表明它是深拷贝还是浅拷贝。因此,它可能永远不会以完全相同的方式使用。
我认为公共复制构造函数对这个问题也没有更清晰的解释。
话虽如此,我建议引入一个适合您的方法系统并传达意图(类似于某种自我记录)。
这取决于所涉及类的复制语义,开发人员应该自己定义。选择的方法通常基于类的预期用例。也许实现两种方法都有意义。但是两种方法都具有相似的缺点-不清楚它们实现了哪种复制方法。这应该在您的类文档中明确说明。
对我来说:
// myobj is some transparent proxy object
var state = new ObjectState(myobj.State);
// do something
myobject = GetInstance();
var newState = new ObjectState(myobject.State);
if (!newState.Equals(state))
throw new Exception();
改为:
// myobj is some transparent proxy object
var state = myobj.State.Clone();
// do something
myobject = GetInstance();
var newState = myobject.State.Clone();
if (!newState.Equals(state))
throw new Exception();
看起来更清晰的意图陈述。
// get a MyClass object 'myObject' to create a clone
public class MyClass {
public MyClass Copy()
=> Newtonsoft.Json.JsonConvert.DeserializeObject<MyClass >(Newtonsoft.Json.JsonConvert.SerializeObject(this)) ?? new();
}
MyClass myObject = new(MyClass);
MyClass myCopy = myObject.Copy();
IDeepCloneable<T>
存在是有意义的,因为...如果您制作自己的实现,您知道 T 是什么,例如SomeClass : IDeepCloneable<SomeClass> { ... }
。 - Kyle BaranMyFunc(IDeepClonable data)
,那么它可以处理所有可克隆对象,而不仅仅是特定类型的对象。或者,如果你有一组可克隆对象IEnumerable<IDeepClonable> lotsOfCloneables
,那么你可以同时克隆许多对象。但如果你不需要这种功能,那么就不要使用非泛型的方法。 - Simon P Stevens