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

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

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

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

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

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

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

每个字符串都必须拥有自己的字符串数据,以及实现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); } } 

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

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

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

当然,没有什么能阻止.NET BCL团队在未来的.NET版本中改变这种行为。 它不会对现有代码产生任何影响。

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

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

s1 + s2 =>新字符串实例(temp1)

temp1 + s3 =>新字符串实例(temp2)

res是对temp2的引用。