2

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

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

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

为什么不?类型系统中的这样一个特性是否太难实现?它会导致不一致吗?我希望准确键入的复制操作是否被视为极端情况?有没有其他方法可以实现我错过的?

PS:请注意,这个问题不是关于浅拷贝和深拷贝的区别如何拷贝,或者根本不做

4

4 回答 4

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() { /*...*/ }
}
于 2013-08-13T09:30:27.680 回答
1

复制这个词的含义总是模棱两可。

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

采用像 Java 这样的语言,它只有原始类型和引用,并采用如下类:

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

现在,当您谈论复制上述类的对象时,您可能想要一个deep copy,因为您希望您的数组都是重复的(如果您更改复制对象中的数组元素,源对象不会被影响。

取而代之的是以下情况:

class PairOfStrings {
   String s1;
   String s1;
}

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

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

class Wheel {
   Car parentCar;
}

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

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

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

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

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

于 2013-08-13T09:33:05.497 回答
0

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

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

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

于 2013-08-13T09:32:00.443 回答
0

我又在思考这个问题,个人得出的结论是这样的特性没有多大意义,原因如下:

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

  • 我的对象1
  • 我的对象2
  • 我的对象3

您的要求是:

  • 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 语言不支持此类功能的思路(我可能错了)。

于 2013-11-13T05:47:36.117 回答