为什么没有ICloneable <t>?</t>

时间:2009-02-11 11:13:57

标签: c# .net icloneable

是否存在通用ICloneable<T>不存在的特殊原因?

如果我每次克隆某些东西都不需要施放它,那会更舒服。

9 个答案:

答案 0 :(得分:136)

除了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>?你很快就开始实现了很多相同的接口...... 与演员相比......真的很糟糕吗?

答案 1 :(得分:104)

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

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

答案 2 :(得分:17)

我需要问一下,除了之外的接口你究竟会做什么呢?接口通常仅在您转换为它时才有用(即此类支持'IBar'),或者具有接受它的参数或设置器(即我采用'IBar')。使用ICloneable - 我们经历了整个框架,并且未能在任何地方找到一个不同于其实现的单一用法。我们也未能在“现实世界”中找到任何除了实现它之外的其他用法(在我们有权访问的~60,000个应用程序中)。

现在,如果您只想强制执行您希望“可复制”对象实现的模式,那么这是一个非常好的用法 - 并继续。您还可以确定“克隆”对您意味着什么(即深或浅)。但是,在这种情况下,我们(BCL)不需要定义它。当需要交换类型为不相关库之间的抽象的实例时,我们只在BCL中定义抽象。

David Kean(BCL团队)

答案 3 :(得分:12)

我认为“为什么”这个问题是不必要的。有很多接口/类/等...这非常有用,但不是.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 ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); }
        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(); }
}

答案 4 :(得分:8)

如果你需要它,write the interface yourself非常容易:

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

答案 5 :(得分:8)

最近阅读了文章Why Copying an Object is a terrible thing to do?,我认为这个问题需要额外的批评。这里的其他答案提供了很好的建议,但答案仍然不完整 - 为什么没有ICloneable<T>

  1. <强>用法

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

    然后,您可以获得一个检查对象is ICloneable的方法。现在怎么办?您无法执行is ICloneable<>,并且由于您不知道编译类型的对象类型,因此无法使该方法具有通用性。第一个真正的问题。

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

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

  2. <强>点

    您希望ICloneable<T>获得舒适感,但舒适性不是界面的设计目的,它们的意义(通常是OOP)来统一对象的行为(尽管在C#中,它仅限于统一外部行为,例如方法和属性,而不是它们的工作方式。)

    如果第一个原因尚未说服你,你可以反对ICloneable<T>也可以限制性地工作,以限制克隆方法返回的类型。然而,讨厌的程序员可以实现ICloneable<T>,其中T不是实现它的类型。因此,要实现限制,可以为泛型参数添加一个很好的约束:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    肯定比没有where的人更具限制性,你仍然不能限制 T 是实现界面的类型(你可以从不同类型的ICloneable<T>派生出来实现它)。

    你知道,即使这个目的也无法实现(原来的ICloneable也失败了,没有任何界面可以真正限制实现类的行为。)

  3. 正如您所看到的,这证明了通用界面难以完全实现,也非常不需要和无用。

    但回到这个问题,你真正寻求的是克隆一个物体时要有安慰。有两种方法可以做到:

    其他方法

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

    此解决方案为用户提供了舒适和预期的行为,但实施起来也太长了。如果我们不想让“舒适”方法返回当前类型,那么只需要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;
    }
    

    它被命名为 Copy ,不会与当前的 Clone 方法冲突(编译器更喜欢类型自己声明的方法而非扩展方法)。 class约束适用于速度(不需要空检查等)。

    我希望这澄清了为什么不制作ICloneable<T>的原因。但是,建议不要实施ICloneable

答案 6 :(得分:2)

虽然这个问题非常陈旧(写下这个答案已经过了5年:)并且已经回答了,但我发现这篇文章很好地回答了问题,请检查here

修改

以下是回答问题的文章的引用(请务必阅读完整的文章,其中包含其他有趣的内容):

  

互联网上有很多引用2003年博客文章的文章   作者:布拉德艾布拉姆斯(Brad Abrams) - 当时在微软工作 - 其中一些人   讨论了关于ICloneable的想法。可以找到博客条目   在这个地址:Implementing ICloneable。尽管有误导性   标题,这个博客条目主要是调用不实现ICloneable   因为浅/深的困惑。文章以直线结尾   建议:如果您需要克隆机制,请定义您自己的克隆,或   复制方法,并确保您明确记录是否是   深或浅的副本。适当的模式是:public <type> Copy();

答案 7 :(得分:1)

一个很大的问题是他们不能将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

答案 8 :(得分:0)

这是一个非常好的问题......你可以创造自己的问题:

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

安德烈说它被认为是一个糟糕的API,但我还没有听说过这个界面被弃用了。这将打破大量的接口...... Clone方法应该执行浅拷贝。 如果对象还提供深层复制,则可以使用重载克隆(bool deep)。

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

class C
{
  public C ( C prototype )
  {
    ...
  }
}

这将删除任何潜在的冗余代码实现情况。 BTW,谈论ICloneable的局限性,是不是真的取决于对象本身来决定是否应该执行浅克隆或深克隆,甚至是部分浅/部分深克隆?我们真的应该关心,只要对象按预期工作吗?在某些情况下,良好的克隆实现可能包括浅层和深层克隆。