为什么没有ICloneable <T>?
为什么一个通用的ICloneable<T>
不存在的特定原因?
如果我不需要每次克隆一些东西,就可以更舒服一些。
ICloneable现在被认为是一个糟糕的API,因为它没有指定结果是深层还是浅层。 我想这就是为什么他们不改善这个接口。
你可能可以做一个types化的克隆扩展方法,但是我认为它需要一个不同的名字,因为扩展方法的优先级比原来的要低。
除了Andrey的回复(我同意,+1) – 当ICloneable
完成时,您还可以select显式实现来使公共Clone()
返回一个types化对象:
public Foo Clone() { /* your code */ } object ICloneable.Clone() {return Clone();}
当然,还有一个通用的ICloneable<T>
inheritance的问题。
如果我有:
public class Foo {} public class Bar : Foo {}
而且我实现了ICloneable<T>
,那么我是否实现了ICloneable<Foo>
? ICloneable<Bar>
? 你很快就开始实施许多相同的接口…比较铸造…是真的很糟糕?
我需要问的是, 除了执行它之外,您还将如何处理接口? 接口通常只在你施放时才有用(即,这个类是否支持'IBar'),或者有参数或设置器接受它(即我采取'IBar')。 使用ICloneable – 我们浏览了整个框架,并没有find任何一个用途,除了它的实现。 我们也没有发现在“真实世界”中的任何用法,除了实现它之外(在我们可以访问的大约60,000个应用程序中)。
现在,如果你只是想强制一个模式,你希望你的“可复制”的对象来实现,这是一个完全不错的用法 – 并继续前进。 你也可以决定“克隆”到底意味着什么(即深或浅)。 但是,在这种情况下,我们(BCL)就没有必要去定义它。 我们只在BCL中定义抽象,当需要交换在不相关的库之间抽象的实例时。
David Kean(BCL团队)
我认为“为什么”这个问题是不必要的。 有很多接口/类/等…这是非常有用的,但不是.NET Framework的基础库的一部分。
但是,主要你可以自己做。
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(); } }
如果您需要, 自己编写界面非常简单:
public interface ICloneable<T> : ICloneable where T : ICloneable<T> { new T Clone(); }
最近看了文章为什么复制一个对象是一件可怕的事情呢? ,我想这个问题需要额外的确认。 其他答案在这里提供了很好的意见,但答案还不完整 – 为什么没有ICloneable<T>
?
-
用法
所以,你有一个实现它的类。 虽然以前你有一个想要
ICloneable
的方法,但现在必须是通用的才能接受ICloneable<T>
。 你需要编辑它。然后,你可以有一个方法来检查一个对象
is ICloneable
。 现在怎么办? 你不能做的is ICloneable<>
,因为你不知道在编译types的对象的types,你不能使该方法通用。 首先真正的问题。所以你需要有
ICloneable<T>
和ICloneable
,前者执行后者。 因此,实现者需要实现这两种方法 –object Clone()
和T Clone()
。 不,谢谢,我们已经有了足够的乐趣与IEnumerable
。正如已经指出的那样,还有inheritance的复杂性。 虽然协方差看起来可以解决这个问题,但派生types需要实现自己types的
ICloneable<T>
,但是已经有一个方法,它具有相同的签名(基本上是参数) – 基类的Clone()
。 使您的新克隆方法界面显式化是毫无意义的,您将失去创buildICloneable<T>
时所寻求的优势。 所以添加new
关键字。 但是不要忘记,您还需要重写基类“Clone()
(对于所有派生类,实现必须保持一致,即从每个克隆方法返回相同的对象,所以基本克隆方法必须是virtual
)! 但是,不幸的是,你不能同时使用相同的签名override
和new
方法。 select第一个关键字时,添加ICloneable<T>
时会失去您想要的目标。 第二个问题,你会打破接口本身,使相同的方法返回不同的对象。 -
点
你想
ICloneable<T>
的舒适,但舒适不是什么接口的devise,其含义是(一般OOP)来统一对象的行为(虽然在C#中,它是有限的统一外部行为,例如方法和属性,而不是他们的工作)。如果第一个原因还没有说服你,你可以反对
ICloneable<T>
也可以限制工作,限制从克隆方法返回的types。 但是,令人讨厌的程序员可以实现ICloneable<T>
,其中T不是实现它的types。 所以,为了达到你的限制,你可以给generics参数添加一个好的约束:
public interface ICloneable<T> : ICloneable where T : ICloneable<T>
当然还有一个没有where
限制,你还是不能限制T是实现接口的types(你可以从实现它的不同types的ICloneable<T>
中派生出来)。你看,即使这个目的不可能实现(原来的
ICloneable
也失败了,没有接口可以真正限制实现类的行为)。
正如你所看到的,这certificate了使通用接口很难完全实现,而且也是不必要的和无用的。
但是回到这个问题,你真正想要的是在克隆一个物体的时候有一个舒适的感觉。 有两种方法可以做到这一点:
其他方法
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(); } }
该解决scheme为用户提供了舒适性和预期的行为,但实施起来也太长了。 如果我们不想让“舒适的”方法返回当前的types,那么只需public virtual object Clone()
就简单多了。
那么让我们来看看“终极”解决scheme – 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; }
它被命名为复制不与当前的克隆方法相冲突(编译器更喜欢types本身的声明方法而不是扩展方法)。 class
约束是有速度的(不需要空检查等)。
我希望澄清一下为什么不制作ICloneable<T>
。 但是,build议不要实现ICloneable
。
一个很大的问题是他们不能把T限制在同一个class级。 例如什么会阻止你这样做:
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
虽然问题是很老的(从写这个答案5年),并已经回答,但我发现这篇文章很好地回答了这个问题, 在这里检查
编辑:
下面是文章中的引用来回答这个问题(确保阅读完整的文章,其中包括其他有趣的事情):
互联网上有很多关于Brad Abrams的博客文章,当时在微软工作的时候,他们讨论了关于ICloneable的一些想法。 博客条目可以在这个地址find: 实现ICloneable 。 尽pipe有误导性的标题,但这篇博客文章却呼吁不要实施ICloneable,主要是因为浅层/深层的混淆。 文章以一个直接的build议结束:如果您需要克隆机制,请定义您自己的克隆或复制方法,并确保您清楚地logging它是深层还是浅层副本。 一个合适的模式是:
public <type> Copy();
这是一个非常好的问题,但是你可以自己创build:
interface ICloneable<T> : ICloneable { new T Clone ( ); }
安德烈说,这被认为是一个糟糕的API,但我没有听到任何有关这个接口将被弃用。 这将打破大量的接口…克隆方法应该执行浅拷贝。 如果对象也提供深度复制,则可以使用重载的克隆(深度)。
编辑:我用于“克隆”一个对象的模式,是在构造函数中传递一个原型。
class C { public C ( C prototype ) { ... } }
这消除了任何潜在的冗余代码实施情况。 顺便说一句,关于ICloneable的局限性,是不是真的要由对象本身来决定是否应该执行浅层克隆还是深层克隆,或者甚至是部分浅层/部分深层的克隆呢? 我们真的应该关心,只要对象按照预期工作? 在某些情况下,一个好的克隆实现可能包括浅层和深层克隆。