为什么.NET会创建新的子字符串而不是指向现有的字符串?

时间:2009-07-04 15:42:31

标签: c# .net string memory string-interning

从使用Reflector的简短看看,看起来String.Substring()为每个子字符串分配内存。我是否纠正这种情况?我认为没有必要,因为字符串是不可变的。

我的基本目标是创建一个IEnumerable<string> Split(this String, Char)扩展方法,不分配额外的内存。

5 个答案:

答案 0 :(得分:22)

大多数使用不可变字符串的语言创建新子字符串而不是引用现有字符串的原因之一是因为这会干扰以后垃圾收集这些字符串。

如果字符串用于其子字符串,但随后更大的字符串变得无法访问(通过子字符串除外)会发生什么。较大的字符串将无法收集,因为这会使子字符串无效。从长远来看,在短期内保存内存的好方法似乎是内存泄漏。

答案 1 :(得分:2)

不使用String类在内部.net中查找。你必须传递对一个可变的数组的引用,并确保没有人搞砸了。

.Net会在每次要求时创建一个新字符串。唯一的例外是由编译器创建的内部字符串(可以由你完成),这些字符串被放入内存一次,然后出于内存和性能原因建立指针到字符串。

答案 2 :(得分:1)

每个字符串都必须拥有它自己的字符串数据,以及实现String类的方式。

您可以创建自己的SubString结构,该结构使用字符串的一部分:

public struct SubString {

   private string _str;
   private int _offset, _len;

   public SubString(string str, int offset, int len) {
      _str = str;
      _offset = offset;
      _len = len;
   }

   public int Length { get { return _len; } }

   public char this[int index] {
      get {
         if (index < 0 || index > len) throw new IndexOutOfRangeException();
         return _str[_offset + index];
      }
   }

   public void WriteToStringBuilder(StringBuilder s) {
      s.Write(_str, _offset, _len);
   }

   public override string ToString() {
      return _str.Substring(_offset, _len);
   }

}

你可以使用其他方法来充实它,比如也可以在不提取字符串的情况下进行比较。

答案 3 :(得分:0)

因为字符串在.NET中是不可变的,所以每个导致新字符串对象的字符串操作都会为字符串内容分配一个新的内存块。

理论上,在提取子字符串时可以重用内存,但这会使垃圾收集非常复杂:如果原始字符串被垃圾收集怎么办?共享一部分的子字符串会发生什么?

当然,没有什么可以阻止.NET BCL团队在将来的.NET版本中更改此行为。它不会对现有代码产生任何影响。

答案 4 :(得分:0)

除了字符串是不可变的,你应该是以下代码片段将在内存中生成多个String实例。

String s1 = "Hello", s2 = ", ", s3 = "World!";
String res = s1 + s2 + s3;

s1 + s2 =&gt;新字符串实例(temp1)

temp1 + s3 =&gt;新字符串实例(temp2)

res是对temp2的引用。