15

.NET の初期の頃から、StringBuilder で ToString を呼び出すと、StringBuilder が使用する内部 char バッファーを使用して (返される) 新しい文字列オブジェクトを提供していたことをはっきりと覚えています。このように、StringBuilder を使用して巨大な文字列を構築した場合、ToString を呼び出してコピーする必要はありません。

その際、StringBuilder はバッファーへの追加の変更を防止する必要がありました。これは、バッファーが不変の文字列によって使用されるようになったためです。その結果、StringBuilder は「copy-on-change」に切り替わり、変更を試みると最初に新しいバッファーが作成され、古いバッファーの内容がコピーされてから変更されます。

StringBuilder を使用して文字列を作成し、通常の文字列に変換して破棄するという前提があったと思います。私には合理的な仮定のように思えます。

これが問題です。ドキュメントでこれについての言及が見つかりません。しかし、それが文書化されたことがあるかどうかはわかりません。

そこで、Reflector (.NET 4.0) を使用して ToString の実装を調べたところ、バッファを共有するだけでなく、実際に文字列をコピーしているように思えます。

[SecuritySafeCritical]
public override unsafe string ToString()
{
    string str = string.FastAllocateString(this.Length);
    StringBuilder chunkPrevious = this;
    fixed (char* str2 = ((char*) str))
    {
        char* chPtr = str2;
        do
        {
            if (chunkPrevious.m_ChunkLength > 0)
            {
                char[] chunkChars = chunkPrevious.m_ChunkChars;
                int chunkOffset = chunkPrevious.m_ChunkOffset;
                int chunkLength = chunkPrevious.m_ChunkLength;
                if ((((ulong) (chunkLength + chunkOffset)) > str.Length) ||     (chunkLength > chunkChars.Length))
                {
                    throw new ArgumentOutOfRangeException("chunkLength",     Environment.GetResourceString("ArgumentOutOfRange_Index"));
                }
                fixed (char* chRef = chunkChars)
                {
                    string.wstrcpy(chPtr + chunkOffset, chRef, chunkLength);
                }
            }
            chunkPrevious = chunkPrevious.m_ChunkPrevious;
        }
        while (chunkPrevious != null);
    }
    return str;
}

さて、前に述べたように、.NET. 私はこの本で の言及さえ見つけました。

私の質問は、この動作は削除されましたか? もしそうなら、誰かが理由を知っていますか?それは私にとって完全に理にかなっていました...

4

5 に答える 5

5

はい、あなたは正しく覚えています。このStringBuilder.ToStringメソッドは、内部バッファを文字列として返し、使用済みとしてフラグを立てるために使用されていたため、 への追加の変更StringBuilderで新しいバッファを割り当てる必要がありました。

これは実装の詳細であるため、ドキュメントには記載されていません。これが、クラスの定義された動作を壊すことなく、基になる実装を変更できる理由です。

投稿されたコードからわかるように、単一の内部バッファーはもうありません。代わりに、文字はチャンクに格納され、ToStringメソッドはチャンクを文字列にまとめます。

この実装の変更の理由は、StringBuilderクラスが実際にどのように使用されているかについての情報を収集し、このアプローチが平均的な状況と最悪の状況の間で重み付けされたより良いパフォーマンスを提供するという結論に達した可能性があります。

于 2010-11-12T15:42:33.753 に答える
5

はい、これは .NET 4.0 用に完全に再設計されました。現在は、文字列ビルダーのリンク リストであるロープを使用して、増大する内部バッファーを格納しています。これは、初期容量がうまく推測できず、テキストの量が多い場合の問題の回避策です。これにより、使用されていない内部バッファーのコピーが大量に作成され、Large Object Heap が詰まります。参照ソースから入手できるソース コードからのこのコメントは関連があります。

    // We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure.
    // Making the maximum chunk size big means less allocation code called, but also more waste 
    // in unused characters and slower inserts / replaces (since you do need to slide characters over
    // within a buffer).
    internal const int MaxChunkSize = 8000;
于 2010-11-12T16:33:51.667 に答える
2

以下は、StringBuilder.ToStringReflectorの .NET 1.1 実装です。

public override string ToString()
{
    string stringValue = this.m_StringValue;
    int currentThread = this.m_currentThread;
    if ((currentThread != 0) && (currentThread != InternalGetCurrentThread()))
    {
        return string.InternalCopy(stringValue);
    }
    if ((2 * stringValue.Length) < stringValue.ArrayLength)
    {
        return string.InternalCopy(stringValue);
    }
    stringValue.ClearPostNullChar();
    this.m_currentThread = 0;
    return stringValue;
}

私が見る限り、場合によっては文字列をコピーせずに返します。StringBuilderただし、が不変になるとは思いません。代わりに、への書き込みを続けると、コピーオンライトが使用されると思いますStringBuilder

于 2010-11-12T15:46:39.857 に答える
0

これは、 によって提供されるインターフェイスに関する文書化された制約ではなく、実装の詳細である可能性が最も高いStringBuilder.ToStringです。文書化されたことがあるかどうか確信が持てないという事実は、これが事実であることを示唆している可能性があります.

多くの場合、書籍では実装の詳細が示され、何かの使用方法に関する洞察が示されますが、ほとんどの場合、実装が変更される可能性があるという警告が表示されます。

実装の詳細に決して依存してはならない理由の良い例です。

ビルダーを不変にするのは機能ではなく、単に の実装の副作用だと思いToStringます。

于 2010-11-12T15:39:13.113 に答える
0

これは前に見たことがなかったので、私の推測は次のとおりです。 a の内部ストレージはStringBuilder、単純なstringではなく、「チャンク」のセットのように見えます。ToStringこの内部文字列は存在しないため、この内部文字列への参照を返すことはできません。

(バージョン 4.0 の StringBuilders は今ロープですか?)

于 2010-11-12T15:40:16.857 に答える