为什么String.Clone()方法返回的是原始字符串而不是它的副本?

21

令人惊讶的是,String.Clone()并不像String.Copy()一样返回字符串的副本。相反,它返回'this',也就是原始字符串。

我想了解为什么 .Net Framework 团队选择了这种方式。

根据 MSDN 的说法:

ICloneable 接口 [...] 要求你实现的 Clone 方法返回当前对象实例的副本。

String.Clone() 明显没有遵循这个指导方针。

我知道字符串是不可变的,但如果不变性是这里的原因,String.Copy() 也会返回 this,但它并没有。

当然,这是一个比较理论的问题。


3
由于字符串是不可变的且进入了内部缓存,因此在与您的问题相关时,string.clone 和 string.copy 没有区别。 - Sam Axe
4
这个问题似乎不是关于实际编程问题的,而更像是一个学术性的问题,因此它看起来与主题无关。 - Servy
4
通过问“如何在我的自定义不可变类型上实现ICloneable?”,可以将此问题轻松转化为一个实际问题。 - user743382
1
@hvd 这将使其主要基于观点,并且很可能需要讨论具体问题;您是否有充分的理由需要深度复制,浅表复制是否足够满足您的需求,首先实现 ICloneable 的原因是什么? - Servy
2
@hvd 是否应该将其定义为深拷贝/浅拷贝是一种观点,这正是您提出更改问题的建议。就其目前状态而言,该问题并非基于观点,而是由于完全不同的原因而偏离了主题。 - Servy
显示剩余9条评论
3个回答

5
如何检测它们之间的区别?只能通过使用object.ReferenceEquals比较这两个引用来检测。但是通过对字符串进行任何语义操作都无法区分它们之间的差异。
通过引用比较字符串几乎总是一个错误,因为你很少能依赖于是否发生了内部化。
这个问题不仅适用于String。如果你有一个不可变的Point类,为什么要从Clone中返回一个新对象呢?没必要。 IClonable很少被使用,而且很少有用。如果你想向你的类的用户公开一种获取给定实例的副本的方法,你根本不需要继承IClonable

2

IClonable已经有些过时,因为从系统整体的角度来看,“克隆”意味着什么并不清楚(深层、浅层等)。请参阅http://blogs.msdn.com/b/brada/archive/2003/04/09/49935.aspx

在参考源代码中,用以下注释记录了Clone方法:

// 没有克隆字符串的必要,因为它们是不可变的,所以我们只需返回“this”。

字符串的内部化意味着很难收集字符串(它们可能被引用多次),这意味着真正创建一个新副本的字符串只会给系统带来压力。此外,内部化和复制是相互冲突的-所以内部化的一般规则胜出。


好的,没有什么比参考来源更能证实其他人的猜测了。 - BoltClock

1
如前所述,由于字符串是只读的,因此Clone()的行为是合理的。实际上你几乎永远不需要两个独立的字符串实例,并且通过不进行复制可以节省内存。在极少数情况下,如果你确实需要复制(出于某种原因想要Object.ReferenceEquals返回false),则可以使用String.Copy()。
似乎没有意义的一个方法只返回this。拥有这样一个方法的原因是为了实现ICloneable接口,我认为String应该实现ICloneable接口,以便像……
T Foo<T>(T x, ...) where T:ICloneable {/* code that might clone x*/}

可以与字符串兼容。

对我来说,这个方法是公共的有点奇怪,因为没有理由直接调用它。如果只能通过对 ICloneable 的引用 访问,那么它就有意义了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接