为什么没有ICloneable<T>?

泛型ICloneable<T>不存在是否有特殊原因?

如果我不需要每次克隆时都强制施放它,那就舒服多了。

54364 次浏览

ICloneable现在被认为是一个糟糕的API,因为它没有指定结果是深度复制还是浅复制。我认为这就是为什么他们不改进这个界面的原因。

您可能可以使用类型化克隆扩展方法,但我认为它需要一个不同的名称,因为扩展方法的优先级比原始方法低。

如果你需要的话,自己编写接口非常简单:

public interface ICloneable<T> : ICloneable
where T : ICloneable<T>
{
new T Clone();
}

除了Andrey的回复(我同意,+1)-当ICloneable 完成时,你还可以选择显式实现,使公共Clone()返回一个类型化对象:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

当然,泛型ICloneable<T>还有第二个问题——继承。

如果我有:

public class Foo {}
public class Bar : Foo {}
我实现了ICloneable<T>,那么我实现了ICloneable<Foo>吗?ICloneable<Bar>吗?你很快就开始实现许多相同的接口…… 与演员相比……真的有那么糟糕吗?< / p >

这是一个非常好的问题……不过,你也可以自己制作:

interface ICloneable<T> : ICloneable
{
new T Clone ( );
}
安德烈说它被认为是一个糟糕的API,但我没有听到任何关于这个接口被弃用的消息。这将破坏大量的接口…… Clone方法应该执行浅拷贝。 如果该对象还提供深度复制,则可以使用重载Clone (bool deep)

编辑:模式我用于“克隆”一个对象,是在构造函数中传递一个原型。

class C
{
public C ( C prototype )
{
...
}
}
这将删除任何潜在的冗余代码实现情况。 顺便说一下,谈到ICloneable的局限性,是否应该执行浅克隆或深克隆,甚至是部分浅克隆/部分深克隆,难道不是由对象本身决定的吗?只要对象按预期工作,我们真的应该关心吗?在某些情况下,一个好的Clone实现很可能同时包含浅克隆和深克隆

我认为“为什么”这个问题;是不必要的。有很多接口/类等…它非常有用,但不是. net Frameworku基础库的一部分。

但是,主要是你可以自己做。

public interface ICloneable<T> : ICloneable {
new T Clone();
}


public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
public abstract T Clone();
object ICloneable.Clone() => return this.Clone();
}


public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
protected abstract T CreateClone();
protected abstract void FillClone(T clone);
public override T Clone() {
T clone = this.CreateClone();
if (clone is null ) {
throw new NullReferenceException( "Clone was not created." );
}


this.FillClone(clone);
return clone
}
}


public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
public string Name { get; set; }


protected override void FillClone( T clone ) {
clone.Name = this.Name;
}
}


public sealed class Person : PersonBase<Person> {
protected override Person CreateClone() => return new Person();
}


public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
public string Department { get; set; }


protected override void FillClone(T clone) {
base.FillClone(clone);


clone.Department = this.Department;
}
}


public sealed class Employee : EmployeeBase<Employee> {
protected override Employee CreateClone() => return new Employee();
}

我需要问,你到底会用接口除了来实现它?接口通常只在你强制转换到它时才有用(即这个类是否支持'IBar'),或者具有接受它的参数或设置(即i接受'IBar')。对于ICloneable,我们遍历了整个框架,没有发现任何一个单独的用法不是它的实现。我们也没有在“现实世界”中找到任何除了实现它之外的用途(在我们可以访问的约6万个应用程序中)。

现在,如果你只是想强制一个你想要你的“可克隆”对象实现的模式,这是一个完全好的用法——去吧。你也可以决定“克隆”对你来说到底意味着什么(即深度克隆还是浅克隆)。然而,在这种情况下,我们(BCL)不需要定义它。只有当需要在不相关的库之间交换抽象类型的实例时,我们才在BCL中定义抽象。

大卫·基恩(BCL队)

一个很大的问题是他们不能限制T是同一个类。举个例子,什么会阻止你这样做:

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


class Dog : IClonable<JackRabbit>
{
//not what you would expect, but possible
JackRabbit Clone()
{
return new JackRabbit();
}


}

它们需要一个参数限制,比如:

interfact IClonable<T> where T : implementing_type

最近读了文章为什么复制对象是一件可怕的事情?,我认为这个问题需要进一步澄清。这里的其他答案提供了很好的建议,但答案仍然不完整——为什么没有ICloneable<T>?

  1. < p > 使用

    你有一个实现它的类。虽然以前你有一个想要ICloneable的方法,但现在它必须是通用的才能接受ICloneable<T>。你需要编辑它。

    然后,你可以有一个方法来检查对象是否is ICloneable。现在该做什么?你不能做is ICloneable<>,因为你不知道编译类型的对象的类型,你不能使方法泛型。第一个真正的问题。

    所以你需要同时拥有ICloneable<T>ICloneable,前者实现后者。因此,实现者需要实现两个方法——object Clone()T Clone()。不,谢谢,我们已经有足够的乐趣IEnumerable

    正如已经指出的,还有继承的复杂性。虽然协方差似乎可以解决这个问题,但派生类型需要实现自己类型的ICloneable<T>,但是已经有一个具有相同签名(基本上是= parameters)的方法-基类的Clone()。使你的新克隆方法接口显式是没有意义的,你将失去你在创建ICloneable<T>时所寻求的优势。所以添加new关键字。但不要忘记,你还需要重写基类的Clone()(实现必须对所有派生类保持一致,即从每个克隆方法返回相同的对象,因此基克隆方法必须是virtual)!但是,不幸的是,你不能同时使用相同签名的overridenew方法。选择第一个关键字,在添加ICloneable<T>时,你会失去你想要的目标。选择第二个,你会破坏接口本身,使应该做相同的方法返回不同的对象

  2. < p >

    你想要ICloneable<T>的舒适,但舒适不是接口设计的目的,它们的意义是(在一般的OOP中)统一对象的行为(尽管在c#中,它仅限于统一外部行为,例如方法和属性,而不是它们的工作)。

    如果第一个原因还没有说服你,你可以反对ICloneable<T>也可以限制性地工作,以限制克隆方法返回的类型。然而,讨厌的程序员可以在T不是实现它的类型时实现ICloneable<T>。因此,为了实现你的限制,你可以在泛型参数中添加一个很好的约束:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T> < br > 当然比没有where的限制更严格,你仍然不能限制T是实现接口的类型(你可以从实现它的不同类型的ICloneable<T>派生)

    你看,即使这个目的也无法实现(原来的ICloneable在这方面也失败了,没有接口可以真正限制实现类的行为)

正如您所看到的,这证明了通用接口很难完全实现,而且真的不需要和无用。

但回到问题上,你真正寻求的是克隆对象时的舒适感。有两种方法:

额外的方法

public class Base : ICloneable
{
public Base Clone()
{
return this.CloneImpl() as Base;
}


object ICloneable.Clone()
{
return this.CloneImpl();
}


protected virtual object CloneImpl()
{
return new Base();
}
}


public class Derived : Base
{
public new Derived Clone()
{
return this.CloneImpl() as Derived;
}


protected override object CloneImpl()
{
return new Derived();
}
}

这个解决方案为用户提供了舒适和预期的行为,但是实现起来太长了。如果我们不想使用“comfortable”方法返回当前类型,那么只使用public virtual object Clone()就容易得多。

那么让我们来看看“终极”解决方案——c#中有什么真正的意图让我们感到舒适?

扩展方法!

public class Base : ICloneable
{
public virtual object Clone()
{
return new Base();
}
}


public class Derived : Base
{
public override object Clone()
{
return new Derived();
}
}


public static T Copy<T>(this T obj) where T : class, ICloneable
{
return obj.Clone() as T;
}

命名为复制是为了不与当前的克隆方法冲突(编译器更喜欢该类型自己声明的方法而不是扩展方法)。class约束的存在是为了速度(不需要空检查等)。

我希望这澄清了为什么不使用ICloneable<T>的原因。然而,建议完全不要实现ICloneable