Reflector を使用して簡単に見るとString.Substring()
、各部分文字列にメモリを割り当てているように見えます。これが事実であるというのは正しいですか?文字列は不変なので、それは必要ないと思いました。
私の根本的な目標は、IEnumerable<string> Split(this String, Char)
追加のメモリを割り当てない拡張メソッドを作成することでした。
Reflector を使用して簡単に見るとString.Substring()
、各部分文字列にメモリを割り当てているように見えます。これが事実であるというのは正しいですか?文字列は不変なので、それは必要ないと思いました。
私の根本的な目標は、IEnumerable<string> Split(this String, Char)
追加のメモリを割り当てない拡張メソッドを作成することでした。
不変の文字列を持つほとんどの言語が、既存の文字列を参照するのではなく、新しい部分文字列を作成する理由の 1 つは、後でこれらの文字列のガベージ コレクションを妨げるためです。
文字列がその部分文字列に使用されているが、より大きな文字列に到達できなくなった場合 (部分文字列を除く) はどうなりますか。部分文字列が無効になるため、より大きな文字列は収集できなくなります。短期的にはメモリを節約するための良い方法のように思えたことが、長期的にはメモリ リークになります。
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);
}
}
文字列を抽出せずに行うこともできる比較などの他の方法で肉付けすることができます。
String は不変であるという点に加えて、次のスニペットはメモリ内に複数の String インスタンスを生成することに注意してください。
String s1 = "Hello", s2 = ", ", s3 = "World!";
String res = s1 + s2 + s3;
s1+s2 => 新しい文字列インスタンス (temp1)
temp1 + s3 => 新しい文字列インスタンス (temp2)
res は temp2 への参照です。
.NET では文字列は不変であるため、結果として新しい文字列オブジェクトが生成されるすべての文字列操作では、文字列の内容に新しいメモリ ブロックが割り当てられます。
理論的には、部分文字列を抽出するときにメモリを再利用することは可能ですが、そうするとガベージ コレクションが非常に複雑になります。元の文字列がガベージ コレクションされたらどうなるでしょうか。その一部を共有する部分文字列はどうなりますか?
もちろん、.NET BCL チームが将来のバージョンの .NET でこの動作を変更することを妨げるものは何もありません。既存のコードには影響しません。