为什么我不能正确键入Clone()?

时间:2013-08-13 09:17:38

标签: c# copy-constructor cloneable

与任何其他C#程序员一样,我面临着如何最好地表达对象复制的问题。 具体来说,我有一个类层次结构,其中所有对象都必须是可复制的。 It has already been discussed是否使用复制构造函数(which C# doesn't provide)或Clone()方法;另一个选择是使用一个获取对象并生成副本的方法。 所有选项都有利有弊,但所有选项都有一个共同点就是输入问题。

我想以任何可能的方式说明在我的类层次结构中的所有对象上都存在一个复制操作,它始终生成与原始相同类型的对象 - 在这样的情况下在编译时报告任何违反此规定的行为。

这三个选项都不允许我这样做,即使是泛型。显然,C#不允许我这样做 ;我也不知道Java和其他一些语言。

为什么不呢?类型系统中的这种功能是否难以实现?它会导致不一致吗? 我是否希望将精确打印的复印操作视为一个极端情况? 是否有其他方法可以实现这一点,我错过了?

PS:请注意,此问题与the difference between shallow and deep copyhow to copywhether to do it at all无关。

4 个答案:

答案 0 :(得分:2)

这里的部分问题是多态性。创建通用ICloneable<T>是微不足道的,但是当你有:

时会遇到问题
Foo : ICloneable<Foo>
Bar : Foo

因为现在Bar需要实现ICloneable<Foo> ICloneable<Bar>。幸运的是,您可以将方法隐藏和多态一起使用,使只是工作,即

interface ICloneable<T> {
    T Clone();
}
class Foo : ICloneable<Foo> {
    protected virtual object Clone() { /*...*/ }
    public Foo Clone() { return (Foo) Clone(); }
}
class Bar : Foo, ICloneable<Bar> {
    protected override object Clone() { /*...*/ }
    public new Bar Clone() { return (Bar) Clone(); }
}

然而!正如您所看到的,维护很快就会变得很痛苦。也许更好的选择是使用内置的非泛型ICloneable和扩展方法:

public static T TypedClone<T>(this T source) where T : class, ICloneable
{
    if(source == null) return null;
    return (T)source.Clone();
}

然后你可以拥有Foo : ICloneable,只需使用:

Foo foo = ...
Foo clone = foo.TypedClone();

仅使用Foo / Bar

class Foo : ICloneable {
    public virtual object Clone() { /*...*/ }
}
class Bar : Foo {
    public override object Clone() { /*...*/ }
}

答案 1 :(得分:1)

复制这个词的含义总是含糊不清。

您的意思是深拷贝还是浅拷贝

采用Java之类的语言,只有原始类型和引用,并采用如下类:

class PairOfArrays {
   int[] i1;
   int[] i2;
}

现在,当您谈到复制上述类的对象时,您可能需要深层复制,因为您希望您的阵列都是重复的(如果您更改复制对象中数组的元素,源对象不会受到影响。

请改为采用以下案例:

class PairOfStrings {
   String s1;
   String s1;
}

如果您复制此对象,是否还需要深层复制? String是java中的一个不可变类型,因此在这种情况下,浅拷贝就足够了,复制的PairOfStrings对象只会引用与源对象相同的字符串。

可能会变得更糟。 有些情况下,使用深拷贝而不是浅拷贝会造成损害。

class Wheel {
   Car parentCar;
}

在上面,parentCar显然是对轮子的父Car对象的引用,所以所有4个轮子都应该指向同一个父对象。

你现在想要复制一个轮子,你最好让浅拷贝你将复制整个车。

正如您所看到的,面向对象编程实现对象的 copy 过程并不是那么明显。

这就是为什么Java使用Clone()方法来定义 copy 的含义,因为它无法自动识别它,这是一个设计问题。

但是,如果您只想要一个对象的深层副本,则可以始终使用序列化。它就像一个魅力。

答案 2 :(得分:0)

如果你想在类层次结构中实现一些克隆逻辑,为什么不像这样声明你自己的接口:

interface IMyCloneable<T>
{
    T Clone();
}

在每种支持克隆的类型中实现它。

答案 3 :(得分:0)

我正在考虑这个问题,并亲自得出结论,由于以下原因,这样的功能没什么意义:

假设您有自己定义的对象层次结构:

  • MyObject1
  • MyObject2
  • MyObject3

您的要求是:

  • MyObject1必须具有MyObject1 clone()方法
  • MyObject2必须具有MyObject2 clone()方法
  • MyObject3必须具有MyObject3 clone()方法

现在,假设您直接使用任何一种方法,如

MyObject2 copy = someMyObject2.clone();

然后,编译器直接对您的方法调用进行类型检查,使您的需求无效。

假设你直接使用它,那么这个要求就没用了,因为你有一个你从未使用过的方法。

最后,假设您要使用一个通用接口,该接口允许您在不知道其类型的情况下复制层次结构的对象:

List<Copyable> list = ...
for( Copyable obj : list ){
   Copyable copy = obj.clone();
}

interface Copyable {
    public Copyable copy();
}

在这种情况下,声明“每个对象必须声明一个返回类型与对象类相同的复制方法”是没有用的,因为无论如何你都不会直接使用对象类。 / p>

对你的问题提出疑问,你为什么要这样的财产? 我认为这是导致OOP语言不支持这种功能的思路(我可能错了)。