2009-07-04 5 views
7

Reflectorを使って簡単に見てみると、String.Substring()は各部分文字列にメモリを割り当てているようです。これが正しいと私は訂正していますか?私は文字列が不変であるので、それは必要ではないと思った。.NETが既存の文字列を指す代わりに新しい部分文字列を作成するのはなぜですか?

私の基本的な目標は、追加のメモリを割り当てないで、IEnumerable<string> Split(this String, Char)拡張メソッドを作成することでした。

+0

ReflectorでStringBuilderの実装を見たことがありますが、IEnumerable Split(StringBuilder、Char)メソッドが機能すると思いますか? – Domenic

+0

Stringの場合。Substring()は新しいメモリを割り当てません。文字列は変更されません –

答えて

22

不変の文字列を持つほとんどの言語が、既存の文字列を参照するのではなく新しい部分文字列を作成する理由の1つは、後でこれらの文字列を収集するガベージコレクションが妨げられるからです。

文字列がその部分文字列に使用されているが、大きな文字列が(部分文字列を除いて)到達不能になるとどうなるか。大きい文字列は、部分文字列を無効にするため、回収不能になります。短期間でメモリを節約する良い方法のように思えるものは、長期的にはメモリリークになります。

+1

私は主な理由は、文字列上のアルゴリズムに関すると思った。文字列が決して変更されないことを安全に仮定できる場合は、安全に参照を渡すことができ、本質的にスレッドセーフです。私はあまりにもガベージコレクションで結びついていると思う。 – Spence

+1

@Spence - これが不変の理由です。これは、文字列間の共有バッファを避ける理由ではありません。不変性とGCを取得したら、スレッドの安全性や既存のアルゴリズムを損なうことなく、背後で共有バッファを簡単に実装できます。 –

2

Stringクラスを使用して.net内部を突き抜けさせることはできません。変更可能な配列への参照を渡す必要があります。

.Netは、要求するたびに新しい文字列を作成します。この例外はコンパイラーによって作成され、メモリーに一度だけ置かれたコンパイラーによって作成されたインメモリーされたストリングです(メモリーとパフォーマンス上の理由から、ポインターがストリングに設定されます)。

0

.NETでは文字列が不変なので、新しい文字列オブジェクトになるすべての文字列操作では、文字列の内容に新しいメモリブロックが割り当てられます。

理論的には、部分文字列を抽出するときにメモリを再利用することは可能ですが、ガベージコレクションが非常に複雑になります。元の文字列がガベージコレクションされている場合それを共有する部分文字列はどうなりますか?

もちろん、.NET BCLチームは.NETの将来のバージョンでこの動作を変更することはできません。既存のコードには何の影響もありません。

+6

JavaのStringは実際にそのようになります。サブ文字列は元の文字列へのポインタに過ぎません。ただし、200-MiB文字列の200文字の部分文字列を取得すると、小さな部分文字列がガベージコレクションされない限り、200-MiB文字列は常にメモリに格納されます。 – Joey

+0

私はそれがこの動作の周りに設計されているので、既存のコードに影響を与える可能性があると思います。もし人々がインターンンで文字列が重複するのを止め、この動作が止まったとすれば、動作中のアプリはメモリの例外を越えて停止する可能性があります。 – Spence

+0

この現象を回避するにはどうすれば設計できますか?文字列の不変性のため、文字列クラスの内部実装が変更された場合に壊れるコードを作成する方法はありません。 –

1

各文字列には、独自の文字列データがあり、Stringクラスの実装方法が必要です。

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); 
    } 

} 

あなたは文字列を抽出することなく行うことも可能である比較のような他の方法でそれを肉付けすることができます

あなたは文字列の一部を使用して、独自のサブストリング構造を作ることができます。

+0

他の部分文字列にはどのような部分文字列がありますか? –

+0

はい、SubString構造体は、それ自体の一部である別の構造体を作成するのは簡単です。 – Guffa

0

文字列が不変であるという点に加えて、次のスニペットがメモリ内に複数のStringインスタンスを生成する必要があります。

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

S1 + S2 =>新しい列インスタンス(TEMP1)

TEMP1 + S3 =>新しい列インスタンス(TEMP2)

RES TEMP2への参照です。

+0

これはコンパイラが最適化できるもののようです。 –

+0

これはコンパイラの問題ではなく、言語を設計する際の選択肢です。 JavaにはStringと同じ規則があります。 System.Text.StringBuilderは、「可変」文字列をシミュレートする優れたクラスです。 –

+1

間違っています - s1 + s2 + s3はString.Concatを1回呼び出すようになります。このため、String.FormatまたはStringBuilder(どちらも比較的遅い)を使用する方が、最大4文字列の方が良いわけではありません。 ILを見て、コンパイラの動作を確認し、プロファイラを使用してプログラムで何がうまく機能しているかを調べます。そうでなければ、「見て、それは靴です!彼は靴を外しました。これは、他に従う他の人も同様にしなければならないサインです!神話のものの代わりに事実の答えを投稿してください。 –

関連する問題