是否存在通用ICloneable<T>
不存在的特殊原因?
如果我每次克隆某些东西都不需要施放它,那会更舒服。
答案 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>
?
<强>用法强>
所以,你有一个实现它的类。虽然之前您的方法需要ICloneable
,但现在必须是通用的才能接受ICloneable<T>
。你需要编辑它。
然后,您可以获得一个检查对象is ICloneable
的方法。现在怎么办?您无法执行is ICloneable<>
,并且由于您不知道编译类型的对象类型,因此无法使该方法具有通用性。第一个真正的问题。
所以你需要同时拥有ICloneable<T>
和ICloneable
,前者实现后者。因此,实施者需要实施两种方法 - object Clone()
和T Clone()
。不,谢谢,我们已经有了IEnumerable
的足够乐趣。
正如已经指出的那样,继承的复杂性也很大。虽然协方差似乎可以解决这个问题,但派生类型需要实现自己类型的ICloneable<T>
,但是已经有一个方法具有相同的签名(基本上是=参数) - Clone()
的基类。使您的新克隆方法界面显式无意义,您将失去创建ICloneable<T>
时所寻求的优势。因此,请添加new
关键字。但是不要忘记你还需要覆盖基类'Clone()
(对于所有派生类,实现必须保持一致,即从每个克隆方法返回相同的对象,因此基本克隆方法具有是virtual
)!但遗憾的是,您不能同时使用override
和new
方法签名。选择第一个关键字后,您将失去添加ICloneable<T>
时想要的目标。选择第二个,你会打破界面本身,使得应该做同样的方法返回不同的对象。
<强>点强>
您希望ICloneable<T>
获得舒适感,但舒适性不是界面的设计目的,它们的意义(通常是OOP)来统一对象的行为(尽管在C#中,它仅限于统一外部行为,例如方法和属性,而不是它们的工作方式。)
如果第一个原因尚未说服你,你可以反对ICloneable<T>
也可以限制性地工作,以限制克隆方法返回的类型。然而,讨厌的程序员可以实现ICloneable<T>
,其中T不是实现它的类型。因此,要实现限制,可以为泛型参数添加一个很好的约束:
public interface ICloneable<T> : ICloneable where T : ICloneable<T>
肯定比没有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();
}
}
此解决方案为用户提供了舒适和预期的行为,但实施起来也太长了。如果我们不想让“舒适”方法返回当前类型,那么只需要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的局限性,是不是真的取决于对象本身来决定是否应该执行浅克隆或深克隆,甚至是部分浅/部分深克隆?我们真的应该关心,只要对象按预期工作吗?在某些情况下,良好的克隆实现可能包括浅层和深层克隆。