为什么可以';t我正确地键入了Clone()

本文关键字:Clone 正确地 为什么 | 更新日期: 2023-09-27 18:27:23

和其他C#程序员一样,我面临着如何最好地表达对象复制的问题。具体来说,我有一个类层次结构,其中所有对象都必须是可复制的。已经讨论过是使用复制构造函数(C#没有提供)还是使用Clone()方法;另一种选择是使用一种方法来获取对象并生成副本。所有选项都有优缺点,但它们共同的一个问题是键入的问题。

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

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

为什么不呢?类型系统中的这样一个功能是否太难实现?它是否会导致不一致?我想要一个准确打字的复印操作被认为是一个角落的情况吗?有没有其他我错过的实现这一目标的方法?

附言:请注意,这个问题根本不是关于浅复制和深复制的区别,如何复制,或者是否复制。

为什么可以';t我正确地键入了Clone()

这里的部分问题是多态性。创建一个通用的ICloneable<T>是微不足道的,但当你有时就会遇到问题

Foo : ICloneable<Foo>
Bar : Foo

因为现在CCD_ 3需要实现CCD_ 4CCD_。幸运的是,您可以同时使用方法隐藏和多态性来使几乎工作,即

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() { /*...*/ }
}

单词的含义总是模糊不清。

您是指深度复制还是浅层复制

以Java这样的语言为例,它只有基元类型和引用,并以这样的类为例

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

现在,当您讨论复制上述类的对象时,您可能想要深度复制

以以下情况为例:

class PairOfStrings {
   String s1;
   String s1;
}

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

情况可能会变得更糟。在某些情况下,使用深层副本而不是浅层副本会造成损害。

class Wheel {
   Car parentCar;
}

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

你现在想复制一个轮子,最好做一个浅复制,你会复制整个汽车。

正如您所看到的,使用面向对象编程来实现对象的复制过程并不那么明显。

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

不过,如果您只想要一个对象的深度副本,您可以始终使用序列化。它就像一个符咒。

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

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

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

我再次思考了这个问题,并个人得出结论,由于以下原因,这样的功能没有什么意义:

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

  • 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();
}

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

所以,你的问题是,你为什么想要这样一处房产?我认为这是导致OOP语言不支持这样一个特性的思路(我可能错了)。