8

从使用反射器的简要介绍来看,它看起来像是String.Substring()为每个子字符串分配内存。我是正确的吗?我认为这没有必要,因为字符串是不可变的。

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

4

5 回答 5

24

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

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

于 2009-07-04T16:29:39.503 回答
2

如果不使用 String 类在 .net 内部进行探索,这是不可能的。您必须传递对可变数组的引用,并确保没有人搞砸。

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

于 2009-07-04T15:49:30.030 回答
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);
   }

}

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

于 2009-07-04T16:08:49.317 回答
0

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

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

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

于 2009-07-04T15:55:04.180 回答
0

再加上字符串是不可变的,您应该是以下代码段将在内存中生成多个字符串实例。

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

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

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

res 是对 temp2 的引用。

于 2009-07-04T19:41:43.200 回答