复制不可变类型是否有意义?

时间:2009-06-06 19:50:47

标签: c# .net immutability

在不可变类型上实现复制方法,返回一个新实例是否有意义?或者它应该只是当前的实例?

我认为类型不会改变,所以为什么要复制?没有人复制数字5,对吧?

6 个答案:

答案 0 :(得分:12)

在某些情况下,它是有道理的。 Java字符串就是一个很好的例子。在Java中创建字符串时,它会引用后备字符数组(char[])。它知道char数组的偏移量和字符串的长度。创建子字符串时,它引用相同的后备数组。现在考虑这段代码:

String x = buildVeryLongString();
String y = x.substring(0, 5);
// Use y a lot, but x is garbage collected

y仍然在系统中的事实意味着仍然需要char[]使用的原始x。换句话说,你使用的内存比你需要的多。如果您将代码更改为:

String x = buildVeryLongString();
String y = new String(x.substring(0, 5));

然后您最终会将数据复制到新的char[]。当xy具有相同的生命周期时,此方法会浪费内存(通过拥有两个副本),但是在x之前对y进行垃圾回收的情况1}},它可以产生很大的不同。

在从字典中读取单词时,我在现实生活中遇到过类似的字符串示例。默认情况下,BufferedReader.readLine()将使用80个字符的缓冲区作为行开头 - 因此readLine()返回的任何(非空)字符串将至少引用char[]数组80个字符。如果你正在阅读每行一个单词的字典文件,那就浪费了很多空间!

这只是一个例子,但它显示了两个不可变对象之间的差异,这些对象在你用它们做什么方面是语义等价的,但在其他方面有不同的特征。 那个通常是你想要复制一个不可变类型的核心 - 但它仍然是一件非常罕见的事情。

在.NET中,字符串的存储方式有所不同 - 字符数据保存在字符串对象本身而不是单独的数组中。 (据我所知,数组,字符串和IntPtr是.NET中唯一的可变大小类型。)但是,字符串中的“缓冲区”仍然可能比它需要的大。例如:

StringBuilder builder = new StringBuilder(10000);
builder.Append("not a lot");
string x = builder.ToString();

x引用的字符串对象将具有巨大的缓冲区。将最后一行更改为builder.ToString().Copy()会使大缓冲区立即符合垃圾回收的条件,而是留下一个小字符串。同样,无条件地执行此操作是一个坏主意,但在某些情况下它可能会有所帮助。

答案 1 :(得分:4)

从技术上讲,整数是一种值类型,因此不断复制。 :)

也就是说,制作一个不可变的对象的副本是没有意义的。其他人提供的字符串示例似乎是这些类的抽象泄漏的乐队助手。

答案 2 :(得分:3)

我假设我们指的是对象(类),因为它是结构的一个没有实际意义的点。

克隆不可变对象有几个可疑的原因:

  • 如果对象远程,并且您需要本地副本(尽管在这种情况下,您可能无法在对象本身上使用实例方法,因为也 em>返回远程实例 - 你必须将克隆方法置于本地(非远程))
  • 如果你非常担心反思(甚至readonly字段可以通过反思改变) - 也许是为了一些超级安全意识的代码
  • 如果某些外部API(你无法控制)使用引用相等,并且你想使用相同的“值”作为两个单独的键 - 好吧,我现在正在伸展东西......

如果我们将讨论扩展到考虑深度克隆,那么它就变得更合理了,因为常规的不可变对象并不意味着任何关联的对象也是不可变的。深度克隆会解决这个问题,但需要单独考虑。

我想也许远程场景是我能做的最好的......

答案 3 :(得分:2)

Java的String类有这个:

String(String original)
      Initializes a newly created String object so that it represents the same 
      sequence of characters as the argument; in other words, the newly created 
      string is a copy of the argument string.

和.Net的Copy()方法也是如此。

这两个框架都是由比我更聪明的人设计的,所以必须有一个很好的理由 - 有时某些人需要不同的参考字符串,但具有相同的价值。

我只是不确定那会是什么时候......

答案 4 :(得分:2)

  

提供“复制”是否有意义   对不可变对象的操作?

没有

(在其他答案中还有很多其他有趣的讨论,但我想我会提供简短的答案。)

如果所述对象需要实现一个需要Clone()方法(或道德等价物)的接口,那么“返回此”就可以了。

答案 5 :(得分:0)

不可变类型的一个优点是它们可以被实现(例如,Java字符串)。当然,如果可以避免,你不应该制作额外的副本。