11

以下のコードでは、オブジェクト参照の等価性をチェックしています。

string x = "Some Text";
string y = "Some Other Text";
string z = "Some Text";

Console.WriteLine(object.ReferenceEquals(x, y)); // False
Console.WriteLine(object.ReferenceEquals(x, z)); // True
Console.WriteLine(object.ReferenceEquals(y, z)); // False

y = "Some Text";

Console.WriteLine(object.ReferenceEquals(x, y)); // True
Console.WriteLine(object.ReferenceEquals(x, z)); // True
Console.WriteLine(object.ReferenceEquals(y, z)); // True

ここ:

  • xおよびz同じオブジェクトを参照します。私はそれxがインターンされ、z使用されているバージョンであると言えます。これについてはよくわかりません。私が間違っている場合は、私を修正してください。
  • yx と同じ値を代入しての値を変更しました。ここで新しいオブジェクトを作成しようとしていると思いました。しかし、私は間違っていました。同じ参照を使用していました。

私の質問は次のとおりです。

  • 使用するすべての文字列に文字列インターン.netを使用しますか?
  • もしそうなら、それはパフォーマンスを損ないませんか?
  • そうでない場合、上記の例でどのように参照が同じになりましたか?
4

4 に答える 4

14

はい、コンパイラの定数文字列式は で処理されldstr、インターンを保証します ( MSDN経由):

Common Language Infrastructure (CLI) は、同じ文字シーケンスを持つ 2 つのメタデータ トークンを参照する 2 つの ldstr 命令の結果が、正確に同じ文字列オブジェクトを返すことを保証します (「文字列インターニング」と呼ばれるプロセス)。

これはすべての文字列ではありません。コード内の定数文字列式です。例えば:

string s = "abc" + "def";

は 1 つの文字列式のみです。IL は "abcdef" の ldstr になります (コンパイラは合成された式を計算できます)。

これによってパフォーマンスが損なわれることはありません。

実行時に生成された文字列は、自動的にインターンされません。次に例を示します。

int i = GetValue();
string s = "abc" + i;

ここでは、「abc」はインターンされていますが、「abc8」はインターンされていません。また、次の点にも注意してください。

char[] chars = {'a','b','c'};
string s = new string(chars);
string t = "abc";

sとは異なる参照であることに注意してtください (リテラル ( に割り当てられるt) はインターンされますが、新しい文字列 ( に割り当てられる) はインターンされsません)。

于 2012-08-17T10:25:41.230 に答える
3

.netは、使用するすべての文字列に文字列インターンを使用しますか?

いいえ。ただし、コード内の定数であるため、コンパイル時に認識している文字列には使用されます。

string x = "abc"; //interned
string y = "ab" + "c"; //interned as the same string because the
                       //compiler can work out that it's the same as
                       //y = "abc" at compile time so there's no need
                       //to do that concatenation at run-time. There's
                       //also no need for "ab" or "c" to exist in your
                       //compiled application at all.
string z = new StreamReader(new FileStream(@"C:\myfile.text")).ReadToEnd();
                       //z isn't interned because it isn't known at compile
                       //time. Note that @"C:\myfile.text" is interned because
                       //while we don't have a variable we can access it by
                       //it is a string in the code.

もしそうなら、それはパフォーマンスを傷つけませんか?

いいえ、パフォーマンスに役立ちます。

まず、これらの文字列はすべて、アプリケーションのメモリのどこかにあります。インターンとは、不要なコピーがないことを意味するため、使用するメモリが少なくなります。2番目:それは、私たちが知っている文字列比較を、インターンされた文字列からのものだけを超高速にします。第三に:それはあまり出てきませんが、それが他の比較に与えるブーストは出てきます。組み込みの比較プログラムの1つに存在する次のコードについて考えてみます。

public override int Compare(string x, string y)
{
    if (object.ReferenceEquals(x, y))
    {
        return 0;
    }
    if (x == null)
    {
        return -1;
    }
    if (y == null)
    {
        return 1;
    }
    return this._compareInfo.Compare(x, y, this._ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
}

これは注文用ですが、同じことが等式/不等式チェックにも当てはまります。2つの文字列が等しいことを確認するか、それらを順番に並べるには、nが文字列の長さに比例するO(n)操作を実行する必要があります(スキップや巧妙さを実行できる場合でも、比例します) 。これは長い文字列の場合は潜在的に遅く、文字列の比較は多くのアプリケーションが多くの時間を行うことであり、速度を上げるのに最適な場所です。等式の場合も最も遅くなります(差を見つけた瞬間に値を返すことができますが、等しい文字列を完全に調べる必要があるため)。

「等しい」の意味を再定義しても、すべてが常にそれ自体と等しくなります(大文字と小文字を区別し、区別せず、異なるカルチャ-すべてがそれ自体と等しく、Equals()従わないオーバーライドを作成すると、バグが発生します)。すべては常にそれが等しいものと同じポイントで注文されます。これは2つのことを意味します:

  1. これ以上の作業をしなくても、いつでも自分と同じものを考えることができます。
  2. 0何かをそれ自体と比較するための比較値は、これ以上の作業なしでいつでも与えることができます。

したがって、上記のコードは、より複雑で費用のかかる比較を行うことなく、この場合のショートカットになります。このケースをカバーしなかった場合、いずれにせよ両方の値が合格した場合のテストを追加する必要があるため、マイナス面もありませnullん。

さて、何かをそれ自体と比較することは、特定のアルゴリズムが機能する方法で非常に頻繁に自然に起こるので、それは常に行う価値があります。ただし、文字列のインターンは、値が異なる2つの文字列(たとえばxz質問の先頭)が実際に同じである時間を増やすため、ショートカットが機能する頻度が高くなります。

ほとんどの場合、これは小さな最適化ですが、無料で入手でき、頻繁に入手できるので、入手するのは素晴らしいことです。これからの実用的なポイント-あなたが書いているEqualsか、Compareあなたもこのショートカットを使うべきかどうかを検討しているなら。

関連する質問は、「私はすべてをインターンするべきですか?」です。

ただし、ここでは、コンパイルされた文字列にはない欠点を考慮する必要があります。文字列をコンパイルすると、どこかにある必要があるため、インターンが無駄になることはありません。ただし、ファイルから文字列を読み取り、それをインターンし、それを二度と使用しない場合、それは長持ちすることになり、それは無駄です。あなたがいつもそれをしたならば、あなたはあなたの記憶の使用を損なう可能性があります。

あなたがいくつかの識別子を含むたくさんのアイテムを頻繁に読んでいると想像してみましょう。これらの識別子を定期的に使用して、アイテムを別のソースからのデータと照合しています。これまでに表示される識別子の小さなセットがあります(たとえば、可能な値は数百個しかない)。次に、等価性チェックがこれらの文字列のすべてであり、それらが多くないため、インターン(読み込まれたデータと比較するデータの両方で-それ以外の場合は無意味です)が優先されます。

または、そのようなオブジェクトが数千あり、一致するデータが常にメモリにキャッシュされているとしましょう。つまり、これらの文字列は常にメモリ内のどこかにあるため、インターンは簡単に勝ちます。(多くの「見つからない」結果の可能性がない限り、一致するものが見つからないためにそれらの識別子をインターンすることは損失です)。

最後に、同じ基本的な手法を別の方法で実行できます。XmlReaderたとえば、比較対象の文字列をNameTableプライベートインターンプールのように機能する文字列に格納しますが、終了時にすべてを収集できます。この手法は、プールされている間は変更されない任意の参照型に適用することもできます(これを不変にして、いつでも変更されないようにするのが最善の方法です)。大量の重複を伴​​う非常に大規模なコレクションでこの手法を使用すると、メモリ使用量を大幅に削減できます(私の最大の節約は少なくとも16GBでした-それ以上になる可能性がありますが、手法が適用される前のその時点でサーバーがクラッシュし続けました)および/または速度比較アップ。

于 2012-08-17T11:10:45.567 に答える
1

私はそれが再び繰り返されると思います

可能性のある重複

奇妙な文字列リテラルの比較

2 つの異なる「文字列」は同じオブジェクト インスタンスですか?

繰り返される

The Common Language Infrastructure (CLI) guarantees that the result of two ldstr instructions referring to two metadata tokens that have the same sequence of characters return precisely the same string object (a process known as "string interning").
于 2012-08-17T10:45:26.763 に答える
1

文字列リテラルは自動的にインターンされます。

プログラムで作成された文字列は、デフォルトではインターンされません (ユーザー入力文字列もそうではありません)。

上記では、「一部のテキスト」と「その他のテキスト」の両方がインターンされており、これらの場所でリテラルを使用しているため、インターンされたバージョンが参照されていることがわかります。

あなたのコードで、あなたが持っている場合:

string.Format("{0} {1}", "Some", "Text")

返された参照が他のリテラルと同じではないことがわかります。

于 2012-08-17T10:25:18.893 に答える