C# 为什么没有 ICloneable<T>?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/536349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why no ICloneable<T>?
提问by Bluenuance
Is there a particular reason why a generic ICloneable<T>
does not exist?
泛型ICloneable<T>
不存在是否有特殊原因?
It would be much more comfortable, if I would not need to cast it everytime I clone something.
如果我不需要每次克隆某些东西时都需要投射它,那会更舒服。
采纳答案by Andrey Shchekin
ICloneable is considered a bad API now, since it does not specify whether the result is a deep or a shallow copy. I think this is why they do not improve this interface.
ICloneable 现在被认为是一个糟糕的 API,因为它没有指定结果是深拷贝还是浅拷贝。我认为这就是他们不改进这个界面的原因。
You can probably do a typed cloning extension method, but I think it would require a different name since extension methods have less priority than original ones.
您可能可以使用类型化克隆扩展方法,但我认为它需要一个不同的名称,因为扩展方法的优先级低于原始方法。
回答by Mauricio Scheffer
It's pretty easy to write the interface yourselfif you need it:
如果需要,自己编写接口非常容易:
public interface ICloneable<T> : ICloneable
where T : ICloneable<T>
{
new T Clone();
}
回答by Marc Gravell
In addition to Andrey's reply (which I agree with, +1) - when ICloneable
isdone, you can also choose explicit implementation to make the public Clone()
return a typed object:
除了安德烈的答复(我同意,+1) -当ICloneable
是做了,你也可以选择明确的实施,使公众Clone()
返回一个类型的对象:
public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}
Of course there is a second issue with a generic ICloneable<T>
- inheritance.
当然,泛型还有第二个问题ICloneable<T>
——继承。
If I have:
如果我有:
public class Foo {}
public class Bar : Foo {}
And I implemented ICloneable<T>
, then do I implement ICloneable<Foo>
? ICloneable<Bar>
? You quickly start implementing a lot of identical interfaces...
Compare to a cast... and is it really so bad?
我实施了ICloneable<T>
,那么我实施ICloneable<Foo>
吗?ICloneable<Bar>
? 你很快就开始实现很多相同的接口......与演员表相比......真的有那么糟糕吗?
回答by baretta
It's a very good question... You could make your own, though:
这是一个很好的问题......不过你可以自己做:
interface ICloneable<T> : ICloneable
{
new T Clone ( );
}
Andrey says it's considered a bad API, but i have not heard anything about this interface becoming deprecated. And that would break tons of interfaces... The Clone method should perform a shallow copy. If the object also provides deep copy, an overloaded Clone ( bool deep ) can be used.
Andrey 说它被认为是一个糟糕的 API,但我没有听说过这个接口被弃用的任何消息。这会破坏大量的接口...... Clone 方法应该执行浅拷贝。如果对象还提供深拷贝,则可以使用重载的 Clone ( bool deep )。
EDIT: Pattern i use for "cloning" an object, is passing a prototype in the constructor.
编辑:我用于“克隆”对象的模式正在构造函数中传递原型。
class C
{
public C ( C prototype )
{
...
}
}
This removes any potential redundant code implementation situations. BTW, talking about the limitations of ICloneable, isn't it really up to the object itself to decide whether a shallow clone or deep clone, or even a partly shallow/partly deep clone, should be performed? Should we really care, as long as the object works as intended? In some occasions, a good Clone implementation might very well include both shallow and deep cloning.
这消除了任何潜在的冗余代码实现情况。顺便说一句,谈到ICloneable的局限性,是否应该由对象本身决定是否应该执行浅克隆或深克隆,甚至部分浅/部分深克隆?只要对象按预期工作,我们真的应该关心吗?在某些情况下,一个好的 Clone 实现很可能包括浅克隆和深克隆。
回答by TcKs
I think the question "why" is needless. There is a lot of interfaces/classes/etc... which is very usefull, but is not part of .NET Frameworku base library.
我认为“为什么”这个问题是不必要的。有很多接口/类/等......这是非常有用的,但不是 .NET Frameworku 基础库的一部分。
But, mainly you can do it yourself.
但是,主要是你可以自己做。
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(); }
}
回答by David Kean
I need to ask, what exactly would you do with the interface other thanimplement it? Interfaces are typically only useful when you cast to it (ie does this class support 'IBar'), or have parameters or setters that take it (ie i take an 'IBar'). With ICloneable - we went through the entire Framework and failed to find a single usage anywhere that was something other than an implementation of it. We've also failed to find any usage in the 'real world' that also does something other than implement it (in the ~60,000 apps that we have access to).
我需要问一下,除了实现它之外,您还会对接口做什么?接口通常仅在您转换为它时有用(即此类是否支持“IBar”),或者具有接受它的参数或设置器(即我使用“IBar”)。使用 ICloneable - 我们浏览了整个框架,但未能在任何地方找到不是它的实现的单一用法。我们也没有在“现实世界”中找到任何除了实现它之外的用途(在我们可以访问的约 60,000 个应用程序中)。
Now if you would just like to enforce a pattern that you want your 'cloneable' objects to implement, that's a completely fine usage - and go ahead. You can also decide on exactly what "cloning" means to you (ie deep or shallow). However, in that case, there's no need for us (the BCL) to define it. We only define abstractions in the BCL when there is a need to exchange instances typed as that abstraction between unrelated libraries.
现在,如果您只想强制执行您希望“可克隆”对象实现的模式,那么这是一个非常好的用法 - 继续。您还可以决定“克隆”对您的确切含义(即深或浅)。但是,在这种情况下,我们(BCL)不需要定义它。当需要在不相关的库之间交换类型为抽象的实例时,我们只在 BCL 中定义抽象。
David Kean (BCL Team)
大卫·基恩(BCL 队)
回答by sheamus
A big problem is that they could not restrict T to be the same class. Fore example what would prevent you from doing this:
一个大问题是他们不能将 T 限制为同一个类。例如什么会阻止你这样做:
interface IClonable<T>
{
T Clone();
}
class Dog : IClonable<HymanRabbit>
{
//not what you would expect, but possible
HymanRabbit Clone()
{
return new HymanRabbit();
}
}
They need a parameter restriction like:
他们需要一个参数限制,如:
interfact IClonable<T> where T : implementing_type
回答by Sameh Deabes
Although the question is very old (5 years from writing this answers :) and was already answered, but I found this article answers the question quite well, check it here
虽然这个问题很老了(从写这个答案开始 5 年了 :) 并且已经回答了,但是我发现这篇文章很好地回答了这个问题,请在此处查看
EDIT:
编辑:
Here is the quote from the article that answers the question (make sure to read the full article, it includes other interesting things):
以下是回答问题的文章引用(请务必阅读全文,其中包含其他有趣的内容):
There are many references on the Internet pointing to a 2003 blog post by Brad Abrams - at the time employed at Microsoft - in which some thoughts about ICloneable are discussed. The blog entry can be found at this address: Implementing ICloneable. Despite the misleading title, this blog entry calls not to implement ICloneable, mainly because of shallow/deep confusion. Article ends in a straight suggestion: If you need a cloning mechanism, define your own Clone, or Copy methodology, and ensure that you document clearly whether it is a deep or shallow copy. An appropriate pattern is:
public <type> Copy();
互联网上有许多参考资料指向 Brad Abrams 于 2003 年撰写的博客文章 - 当时在微软工作 - 其中讨论了关于 ICloneable 的一些想法。可以在以下地址找到博客条目:Implementing ICloneable。尽管标题具有误导性,但这篇博文呼吁不要实现 ICloneable,主要是因为浅/深混淆。文章以直接的建议结尾:如果您需要克隆机制,请定义您自己的克隆或复制方法,并确保您清楚地记录它是深拷贝还是浅拷贝。一个合适的模式是:
public <type> Copy();
回答by IllidanS4 wants Monica back
Having read recently the article Why Copying an Object is a terrible thing to do?, I think this question needs additional clafirication. Other answers here provide good advices, but still the answer isn't complete - why no ICloneable<T>
?
最近阅读了文章为什么复制对象是一件可怕的事情?,我认为这个问题需要额外的澄清。这里的其他答案提供了很好的建议,但答案仍然不完整 - 为什么不ICloneable<T>
?
Usage
So, you have a class that implements it. While previously you had a method that wanted
ICloneable
, it now has to be generic to acceptICloneable<T>
. You would need to edit it.Then, you could have got a method that checks if an object
is ICloneable
. What now? You can't dois ICloneable<>
and as you don't know the type of the object at compile-type, you can't make the method generic. First real problem.So you need to have both
ICloneable<T>
andICloneable
, the former implementing the latter. Thus an implementer would need to implement both methods -object Clone()
andT Clone()
. No, thanks, we already have enough fun withIEnumerable
.As already pointed out, there is also the complexity of inheritance. While covariance may seem to solve this problem, a derived type needs to implement
ICloneable<T>
of its own type, but there is already a method with the same signature (= parameters, basically) - theClone()
of the base class. Making your new clone method interface explicit is pointless, you will lose the advantage you sought when creatingICloneable<T>
. So add thenew
keyword. But don't forget that you would also need to override the base class'Clone()
(the implementation has to remain uniform for all derived classes, i.e. to return the same object from every clone method, so the base clone method has to bevirtual
)! But, unfortunately, you can't bothoverride
andnew
methods with the same signature. Choosing the first keyword, you'd lose the goal you wanted to have when addingICloneable<T>
. Chossing the second one, you'd break the interface itself, making methods that should do the same return different objects.Point
You want
ICloneable<T>
for comfort, but comfort is not what interfaces are designed for, their meaning is (in general OOP) to unify the behavior of objects (although in C#, it is limited to unifying the outer behavior, e.g. the methods and properties, not their workings).If the first reason hasn't convinced you yet, you could object that
ICloneable<T>
could also work restrictively, to limit the type returned from the clone method. However, nasty programmer can implementICloneable<T>
where T is not the type that is implementing it. So, to achieve your restriction, you can add a nice constraint to the generic parameter:public interface ICloneable<T> : ICloneable where T : ICloneable<T>
Certainly more restrictive that the one withoutwhere
, you still can't restrict that Tis the type that is implementing the interface (you can derive fromICloneable<T>
of different type that implements it).You see, even this purpose couldn't be achieved (the original
ICloneable
also fails at this, no interface can truly limit the behavior of the implementing class).
用法
所以,你有一个实现它的类。虽然以前您有一个想要的方法
ICloneable
,但现在它必须是通用的才能接受ICloneable<T>
。你需要编辑它。然后,您可以获得一个方法来检查 object 是否存在
is ICloneable
。现在怎么办?您不能这样做,is ICloneable<>
并且由于您不知道 compile-type 对象的类型,因此您无法使该方法通用。第一个真正的问题。所以你需要同时拥有
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>
也可以限制性地工作,以限制从 clone 方法返回的类型。然而,讨厌的程序员可以实现ICloneable<T>
T 不是实现它的类型。因此,为了实现您的限制,您可以向泛型参数添加一个很好的约束:public interface ICloneable<T> : ICloneable where T : ICloneable<T>
当然比没有的限制更多where
,您仍然不能限制T是实现接口的类型(您可以从ICloneable<T>
不同类型派生)实现它)。你看,连这个目的都达不到(原来的
ICloneable
也失败了,没有任何接口可以真正限制实现类的行为)。
As you can see, this proves making the generic interface is both hard to fully implement and also really unneeded and useless.
正如您所看到的,这证明使通用接口难以完全实现,而且确实不需要和无用。
But back to the question, what you really seek is to have comfort when cloning an object. There are two ways to do it:
但是回到问题,您真正寻求的是在克隆对象时获得安慰。有两种方法可以做到:
Additional methods
附加方法
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();
}
}
This solution provides both comfort and intended behavior to users, but it's also too long to implement. If we didn't want to have the "comfortable" method returning the current type, it is much more easy to have just public virtual object Clone()
.
此解决方案为用户提供舒适度和预期行为,但实施时间也太长。如果我们不想让“舒适”的方法返回当前类型,那么使用public virtual object Clone()
.
So let's see the "ultimate" solution - what in C# is really intented to give us comfort?
那么让我们看看“终极”解决方案——C# 中的什么真正意图给我们带来安慰?
Extension methods!
扩展方法!
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;
}
It's named Copynot to collide with the current Clonemethods (compiler prefers the type's own declared methods over extension ones). The class
constraint is there for speed (doesn't require null check etc.).
它被命名为Copy 是为了不与当前的Clone方法冲突(编译器更喜欢类型自己声明的方法而不是扩展方法)。该class
约束是有速度(不需要空检查等)。
I hope this clarifies the reason why not to make ICloneable<T>
. However, it is recommended not to implement ICloneable
at all.
我希望这能澄清为什么不 make 的原因ICloneable<T>
。但是,建议完全不要执行ICloneable
。