.NETの一意のオブジェクト識別子


118

インスタンスの一意の識別子を取得する方法はありますか?

GetHashCode()同じインスタンスを指す2つの参照で同じです。ただし、2つの異なるインスタンスが(かなり簡単に)同じハッシュコードを取得できます。

Hashtable hashCodesSeen = new Hashtable();
LinkedList<object> l = new LinkedList<object>();
int n = 0;
while (true)
{
    object o = new object();
    // Remember objects so that they don't get collected.
    // This does not make any difference though :(
    l.AddFirst(o);
    int hashCode = o.GetHashCode();
    n++;
    if (hashCodesSeen.ContainsKey(hashCode))
    {
        // Same hashCode seen twice for DIFFERENT objects (n is as low as 5322).
        Console.WriteLine("Hashcode seen twice: " + n + " (" + hashCode + ")");
        break;
    }
    hashCodesSeen.Add(hashCode, null);
}

私はデバッグアドインを作成しています。プログラムの実行中に一意である参照のIDを取得する必要があります。

私はすでにインスタンスの内部ADDRESSを取得できました。これは、ガベージコレクター(GC)がヒープを圧縮する(=オブジェクトを移動する=アドレスを変更する)までは一意です。

スタックオーバーフローの質問Object.GetHashCode()のデフォルト実装が関連している可能性があります。

デバッガーAPIを使用してデバッグされているプログラムのオブジェクトにアクセスしているため、オブジェクトは私の制御下にありません。オブジェクトを制御している場合、自分の一意の識別子を追加するのは簡単です。

ハッシュテーブルID->オブジェクトを作成するための一意のIDを使用して、すでに表示されているオブジェクトを検索できるようにしました。今のところ私はそれをこのように解決しました:

Build a hashtable: 'hashCode' -> (list of objects with hash code == 'hashCode')
Find if object seen(o) {
    candidates = hashtable[o.GetHashCode()] // Objects with the same hashCode.
    If no candidates, the object is new
    If some candidates, compare their addresses to o.Address
        If no address is equal (the hash code was just a coincidence) -> o is new
        If some address equal, o already seen
}

回答:


42

参照、オブジェクトの一意の識別子です。これを文字列などに変換する方法はわかりません。参照の値は(ご覧のように)圧縮中に変更されますが、以前のすべての値Aは値Bに変更されます。安全なコードが関係しているため、それはまだ一意のIDです。

関係するオブジェクトが制御下にある場合、選択したID(GUID、整数など)への参照から(ガベージコレクションの防止を回避するため)弱い参照を使用してマッピングを作成できます。ただし、これにより、ある程度のオーバーヘッドと複雑さが追加されます。


1
ルックアップでは、追跡するすべての参照を反復処理する必要があると思います。同じオブジェクトへのWeakReferenceは互いに等しくないため、他の多くのことはできません。
ロマン・スターコフ2010

1
特にそのようなIDが連続して発行された場合は、各オブジェクトに一意の64ビットIDを割り当てると便利な場合があります。有用性がコストを正当化するかどうかはわかりませんが、このようなことは、2つの異なる不変オブジェクトを比較し、それらが等しいことがわかった場合に役立ちます。可能であれば、新しいものへの参照を古いものへの参照で上書きする場合、同一であるが異なるオブジェクトへの多くの冗長な参照を避けることができます。
スーパーキャット2013年

1
"識別子。" その言葉があなたの考えていることを意味しているとは思いません。
Slipp D. Thompson 2014

5
@ SlippD.Thompson:いいえ、まだ1対1の関係です。特定のオブジェクトを参照する参照値は1つだけです。その値はメモリ内に何度も現れる可能性があります(たとえば、複数の変数の値として)が、それでも単一の値です。それは家の住所のようなものです。私は自宅の住所を複数の紙に複数書き留めることができますが、それでも私の家の識別子です。2つの同一でない参照値、少なくともC#では異なるオブジェクトを参照する必要があります。
Jon Skeet、2014年

1
@supercat:「カプセル化されたアイデンティティ」についての理解は異なるかもしれないと思います-しかし、おそらく私たちがすでに持っている以上のことを誰かに手伝ってもらっていないと思います:)私たちは今までに直接会う...
Jon Skeet 2014年

72

.NET 4以降のみ

皆さん、朗報です!

この仕事に最適なツールは.NET 4に組み込まれており、と呼ばれていConditionalWeakTable<TKey, TValue>ます。このクラス:

  • 辞書のように、任意のデータを管理オブジェクトインスタンスに関連付けるために使用できます(ただし、辞書ではありません)。
  • メモリアドレスに依存しないため、GCによるヒープの圧縮の影響を受けません。
  • テーブルにキーとして入力されたという理由だけでオブジェクトを存続させないため、プロセス内のすべてのオブジェクトを永久に存続させることなく使用できます。
  • 参照の等価性を使用してオブジェクトのアイデンティティを決定します。移動、クラス作成者はこの動作を変更できないため、どのタイプのオブジェクトでも一貫して使用できます
  • オンザフライで入力できるため、オブジェクトコンストラクター内にコードを挿入する必要はありません

5
完全を期すために、その内部の仕組みにConditionalWeakTable依存しRuntimeHelpers.GetHashCode、それobject.ReferenceEqualsを実行します。動作は、IEqualityComparer<T>これら2つの方法を使用するを作成するのと同じです。パフォーマンスが必要な場合ConditionalWeakTableは、スレッドセーフにするためにすべての操作をロックするため、実際にこれを行うことをお勧めします。
atlaste 2014年

1
@StefandeBruijn:A ConditionalWeakTableは、それぞれへの参照を保持します。Valueこれは、対応するへの他の場所で保持されている参照と同じくらいの強さKeyです。a ConditionalWeakTableが唯一の現存する参照を保持するオブジェクトは、キーが存在する場合、自動的に存在しなくなります。
スーパーキャット2014年

41

ObjectIDGeneratorクラスをチェックアウトしましたか?これは、あなたがしようとしていること、およびマークグラベルが説明していることを実行します。

ObjectIDGeneratorは、以前に識別されたオブジェクトを追跡します。オブジェクトのIDを要求すると、ObjectIDGeneratorは、既存のIDを返すか、新しいIDを生成して記憶するかを認識します。

IDは、ObjectIDGeneratorインスタンスの存続期間を通じて一意です。通常、ObjectIDGeneratorの寿命は、それを作成したフォーマッタが存続する限り存続します。オブジェクトIDは、特定のシリアル化されたストリーム内でのみ意味があり、シリアル化されたオブジェクトグラフ内で他のオブジェクトへの参照を持つオブジェクトを追跡するために使用されます。

ObjectIDGeneratorはハッシュテーブルを使用して、どのオブジェクトにどのIDが割り当てられているかを保持します。各オブジェクトを一意に識別するオブジェクト参照は、ランタイムガベージコレクションヒープ内のアドレスです。オブジェクト参照値はシリアル化中に変更される可能性がありますが、テーブルは自動的に更新されるため、情報は正確です。

オブジェクトIDは64ビットの数値です。割り当ては1から始まるため、0が有効なオブジェクトIDになることはありません。フォーマッターは、ゼロ値を選択して、値がnull参照(Visual BasicではNothing)であるオブジェクト参照を表すことができます。


5
Reflectorは、ObjectIDGeneratorがデフォルトのGetHashCode実装に依存するハッシュテーブルであることを示しています(つまり、ユーザーオーバーロードを使用していません)。
Anton Tykhyy 2009

印刷可能な一意のIDが必要な場合は、おそらく最良のソリューションです。
Roman Starkov、2010

ObjectIDGeneratorも電話に実装されていません。
Anthony Wieser 2012年

ObjectIDGeneratorが何をしているのか正確には理解していませんが、RuntimeHelpers.GetHashCodeを使用している場合でも機能しているようです。私は両方をテストしましたが、私の場合、RuntimeHelpers.GetHashCodeのみが失敗します。
DanielBişar12年

+1-(少なくともデスクトップでは)非常に滑らかに動作します。
Hot Licks

37

RuntimeHelpers.GetHashCode()MSDN)を助けるかもしれません。


2
これは役立つかもしれませんが、コストがかかります-IIRC、ベースオブジェクトを使用します。GetHashCode()は、無料ではない同期ブロックを割り当てる必要があります。いい考えですが、+ 1してください。
Jon Skeet、

おかげで、私はこの方法を知りませんでした。ただし、一意のハッシュコードも生成されません(問題のサンプルコードとまったく同じように動作します)。ただし、ユーザーがハッシュコードをオーバーライドする場合は、デフォルトバージョンを呼び出すのに役立ちます。
Martin Konicek、2009

1
それらがあまり必要ない場合は、GCHandleを使用できます(以下を参照)。
Anton Tykhyy 2009

42
非常に尊敬されている著者による.NETの本には、RuntimeHelpers.GetHashCode()がAppDomain内で一意のコードを生成し、MicrosoftがメソッドGetUniqueObjectIDを命名した可能性があると記載されています。これは単に間違っています。テストの結果、オブジェクト(WinForms TextBox)のインスタンスを10,000個作成するまでに、通常は重複が発生し、30,000を超えることはないことがわかりました。想定される一意性に依存するコードは、1/10を超えない数のオブジェクトを作成した後、運用システムで断続的なクラッシュを引き起こしていました。
Jan Hettich、2011

3
@supercat:Aha-2003年から、.NET 1.0および1.1からの証拠がいくつか見つかりました。.NET 2の変更を計画していたようです。blogs.msdn.com
Jon Skeet

7

一瞬であなた自身のものを開発することができます。例えば:

   class Program
    {
        static void Main(string[] args)
        {
            var a = new object();
            var b = new object();
            Console.WriteLine("", a.GetId(), b.GetId());
        }
    }

    public static class MyExtensions
    {
        //this dictionary should use weak key references
        static Dictionary<object, int> d = new Dictionary<object,int>();
        static int gid = 0;

        public static int GetId(this object o)
        {
            if (d.ContainsKey(o)) return d[o];
            return d[o] = gid++;
        }
    }   

System.Guid.NewGuid()などの独自の一意のIDとして保持するものを選択するか、最も高速なアクセスのために単に整数を選択できます。


2
これが必要なのがDisposeバグである場合、これはあらゆる種類の処分を防ぐため、役に立ちません。
ロマン・スターコフ2010

1
これは非常にはObject.equalsに同じ値を返すオブジェクト崩壊、代わりにアイデンティティの辞書の使用平等として動作しません
アンソニーWieserのが

1
これはオブジェクトを存続させます。
Martin Lottering 2013年

1
@MartinLottering ConditionalWeakTable <object、idType>を使用している場合はどうなりますか?
Demetris Leptos 16年

7

この方法はどうですか:

最初のオブジェクトのフィールドを新しい値に設定します。2番目のオブジェクトの同じフィールドに同じ値がある場合、それはおそらく同じインスタンスです。それ以外の場合は、別の方法で終了します。

次に、最初のオブジェクトのフィールドを別の新しい値に設定します。2番目のオブジェクトの同じフィールドが別の値に変更されている場合、それは間違いなく同じインスタンスです。

終了時に最初のオブジェクトのフィールドを元の値に戻すことを忘れないでください。

問題?


4

Visual Studioで一意のオブジェクト識別子を作成することができます。ウォッチウィンドウでオブジェクト変数を右クリックし、[ オブジェクトIDの作成 ]を選択します。し、コンテキストメニューから[ます。

残念ながら、これは手動の手順であり、識別子をコード経由でアクセスできるとは思いません。


Visual Studioのどのバージョンにこの機能がありますか?たとえば、Expressバージョン?
Peter Mortensen 2017年

3

このような識別子は、インスタンス内または外部のいずれかで、手動で手動で割り当てる必要があります。

データベースに関連するレコードの場合、主キーが役立つ場合があります(ただし、重複する可能性があります)。または、を使用するかGuid、独自のカウンターを保持して、を使用して割り当てますInterlocked.Increment(オーバーフローしないように十分に大きくします)。


2

私はこれが答えられたことを知っていますが、あなたが使うことができることに注意することは少なくとも役に立ちます:

http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx

どちらが直接「一意のID」を与えるのではなく、WeakReferences(およびハッシュセット?)と組み合わせると、さまざまなインスタンスを追跡する非常に簡単な方法を提供できます。


1

ここで提供する情報は新しいものではなく、完全を期すために追加しただけです。

このコードの考え方は非常に簡単です。

  • オブジェクトには一意のIDが必要ですが、これはデフォルトではありません。代わりに、次善の策に頼らなければなりません。RuntimeHelpers.GetHashCode、一種の一意のIDを取得することです。
  • 一意性をチェックするには、これを使用する必要があることを意味します object.ReferenceEquals
  • ただし、それでも一意のIDが欲しいので、 GUID、定義上一意である。
  • 必要がない場合はすべてをロックしたくないので、は使用しませんConditionalWeakTable

組み合わせると、次のコードが得られます。

public class UniqueIdMapper
{
    private class ObjectEqualityComparer : IEqualityComparer<object>
    {
        public bool Equals(object x, object y)
        {
            return object.ReferenceEquals(x, y);
        }

        public int GetHashCode(object obj)
        {
            return RuntimeHelpers.GetHashCode(obj);
        }
    }

    private Dictionary<object, Guid> dict = new Dictionary<object, Guid>(new ObjectEqualityComparer());
    public Guid GetUniqueId(object o)
    {
        Guid id;
        if (!dict.TryGetValue(o, out id))
        {
            id = Guid.NewGuid();
            dict.Add(o, id);
        }
        return id;
    }
}

これを使用するには、のインスタンスを作成し、UniqueIdMapperそれがオブジェクトに対して返すGUIDを使用します。


補遺

したがって、ここではもう少し進んでいます。少し書き留めさせてくださいConditionalWeakTable

ConditionalWeakTableいくつかのことを行います。最も重要なことは、ガベージコレクターを気にしないことです。つまり、このテーブルで参照するオブジェクトは関係なく収集されます。オブジェクトを検索すると、基本的には上記の辞書と同じように機能します。

知りたくない?結局のところ、GCによってオブジェクトが収集されているとき、オブジェクトへの参照があるかどうかがチェックされ、ある場合は収集されます。だからからのオブジェクトがある場合ConditionalWeakTable、なぜ参照されたオブジェクトが収集されるのでしょうか。

ConditionalWeakTable他の一部の.NET構造でも使用される小さなトリックを使用します。オブジェクトへの参照を格納する代わりに、実際にはIntPtrを格納します。これは実際の参照ではないため、オブジェクトを収集できます。

したがって、この時点で、対処すべき2つの問題があります。まず、オブジェクトをヒープ上で移動できるため、IntPtrとして何を使用しますか?次に、オブジェクトにアクティブな参照があることをどのようにして知るのでしょうか。

  • オブジェクトをヒープに固定することができ、その実際のポインタを保存できます。GCがオブジェクトを削除するためにヒットすると、ピンを外して収集します。ただし、これは固定されたリソースを取得することを意味します。これは、多くのオブジェクトがある場合(メモリの断片化の問題のため)はお勧めできません。これはおそらくそれがどのように機能するかではありません。
  • GCがオブジェクトを移動すると、GCがコールバックし、参照を更新できます。これは、外部呼び出しによって判断して実装される方法である可能性がありますDependentHandle少し洗練されていると思います。
  • オブジェクト自体へのポインターではなく、GCからのすべてのオブジェクトのリスト内のポインターが格納されます。IntPtrは、このリストのインデックスまたはポインタです。リストは、オブジェクトが世代を変更した場合にのみ変更されます。その時点で、単純なコールバックがポインターを更新できます。Mark&Sweepの動作を覚えている場合、これはより理にかなっています。ピン留めはなく、削除は以前と同じです。これがでの動作方法だと思いますDependentHandle

この最後のソリューションでは、リストバケットが明示的に解放されるまで、ランタイムがリストバケットを再利用しないようにする必要があります。また、ランタイムへの呼び出しによってすべてのオブジェクトを取得する必要もあります。

彼らがこのソリューションを使用していると仮定すると、2番目の問題にも対処できます。Mark&Sweepアルゴリズムは、どのオブジェクトが収集されたかを追跡します。収集され次第、この時点でわかります。オブジェクトがオブジェクトがそこにあるかどうかを確認すると、 'Free'が呼び出され、ポインターとリストエントリが削除されます。オブジェクトは本当になくなっています。

この時点で注意すべき重要な点の1つConditionalWeakTableは、が複数のスレッドで更新され、スレッドセーフではない場合、状況がひどくうまくいかないことです。その結果、メモリリークが発生します。これが、すべてのConditionalWeakTable単純な「ロック」を行うです。これにより、これは発生しません。

もう1つ注意すべき点は、エントリのクリーンアップはときどき発生する必要があることです。実際のオブジェクトはGCによってクリーンアップされますが、エントリはクリーンアップされません。これがConditionalWeakTableサイズが大きくなる理由です。特定の制限に達すると(ハッシュの衝突の可能性によって決定されます)、Resizeオブジェクトをクリーンアップする必要があるかどうかをチェックするがトリガーされます(クリーンアップする必要がある場合)free、GCプロセスで呼び出され、IntPtrハンドルがます。

これがDependentHandle直接公開されない理由でもあると思います。物事をいじってメモリリークを発生させたくないのです。そのための次善の策はWeakReferenceIntPtrオブジェクトの代わりに)ですが、残念ながら「依存関係」の側面は含まれていません。

あとは、メカニックをいじって、依存関係の動作を確認できます。必ず複数回起動して、結果を確認してください。

class DependentObject
{
    public class MyKey : IDisposable
    {
        public MyKey(bool iskey)
        {
            this.iskey = iskey;
        }

        private bool disposed = false;
        private bool iskey;

        public void Dispose()
        {
            if (!disposed)
            {
                disposed = true;
                Console.WriteLine("Cleanup {0}", iskey);
            }
        }

        ~MyKey()
        {
            Dispose();
        }
    }

    static void Main(string[] args)
    {
        var dep = new MyKey(true); // also try passing this to cwt.Add

        ConditionalWeakTable<MyKey, MyKey> cwt = new ConditionalWeakTable<MyKey, MyKey>();
        cwt.Add(new MyKey(true), dep); // try doing this 5 times f.ex.

        GC.Collect(GC.MaxGeneration);
        GC.WaitForFullGCComplete();

        Console.WriteLine("Wait");
        Console.ReadLine(); // Put a breakpoint here and inspect cwt to see that the IntPtr is still there
    }

1
ConditionalWeakTableオブジェクトへの参照が存在する間、オブジェクトの表現のみを永続化するため、Aの方が適している場合があります。また、Int64オブジェクトには永続的なランクを付けることができるため、GUIDはGUIDよりも優れている可能性があることをお勧めします。そのようなことは、ロックのシナリオで役立つかもしれません(たとえば、複数のロックを取得する必要があるすべてのコードがいくつかの定義された順序でそうする場合、デッドロックを回避できますが、それが機能するためには定義された順序が必要です)。
スーパーキャット2014年

@supercat longs については確かです。それはあなたのシナリオに依存します-f.ex. 分散システムでは、で作業する方が便利な場合がありますGUID。に関してはConditionalWeakTable:あなたは正しいです。DependentHandle生きているかどうかをチェックします(注:サイズが変更されたときのみ!)。これは、ここで役立ちます。あなたは、パフォーマンスを必要とする場合でも、ロックは個人の実装嫌い正直Iであることを...そう、その場合には、これを使用することは興味深いかもしれないが、そこに問題になることができConditionalWeakTable、簡単なを使用しての私のバイアスにどのおそらくリードを、Dictionaryでも-あなたは正しいけど。
atlaste 2014年

私は長い間、ConditionalWeakTable実際にどのように機能するかについて知りたがっています。アイテムの追加のみが許可されているという事実から、同時実行性に関連するオーバーヘッドを最小限に抑えるように設計されていると思いますが、内部でどのように機能するかわかりません。DependentHandleあるオブジェクトが別のオブジェクトの存続期間中存続していることを確認することが重要である場合があるので、テーブルを使用しない単純なラッパーがないのは興味深いことですが、後者のオブジェクトには参照の余地がありません。最初に。
スーパーキャット2014年

@supercat私はそれがどのように機能するかについての補遺を掲載します。
atlaste 2014年

ConditionalWeakTableテーブルに格納されたエントリを変更することはできません。そのため、メモリバリアを使用して安全に実装できるが、ロックは実装できないと思います。唯一の問題となる状況は、2つのスレッドが同じキーを同時に追加しようとした場合です。これは、アイテムが追加された後に「add」メソッドがメモリバリアを実行し、スキャンして1つのアイテムにそのキーがあることを確認することで解決できます。複数のアイテムが同じキーを持っている場合、それらの1つは「最初」として識別できるため、他のアイテムを削除することができます。
スーパーキャット2014年

0

あなたが特定の使用のための独自のコードでモジュールを作成している場合は、majkinetorの方法は MIGHTてきました。しかし、いくつかの問題があります。

まず、公式ドキュメントは、が一意の識別子を返すことを保証していませんGetHashCode()Object.GetHashCodeメソッド()を参照)。

等しいハッシュコードがオブジェクトの等価性を暗示するとは限りません。

第二にGetHashCode()ほとんどの場合に機能するように非常に少量のオブジェクトがあると仮定します。このメソッドはいくつかのタイプでオーバーライドできます。
たとえば、クラスCを使用していてGetHashCode()、常に0を返すようにオーバーライドしているとします。その後、Cのすべてのオブジェクトが同じハッシュコードを取得します。残念ながら、DictionaryHashTableおよび他のいくつかの連想コンテナは、このメソッドを使用するようになります:

ハッシュコードは、Dictionary <TKey、TValue>クラス、Hashtableクラス、またはDictionaryBaseクラスから派生したタイプなどのハッシュベースのコレクションにオブジェクトを挿入して識別するために使用される数値です。GetHashCodeメソッドは、オブジェクトの等価性をすばやくチェックする必要があるアルゴリズムにこのハッシュコードを提供します。

したがって、このアプローチには大きな制限があります。

そしてさらに、何が汎用ライブラリーを構築したい場合は?使用したクラスのソースコードを変更できないだけでなく、その動作も予測できません。

JonSimonが回答を投稿してくれたことに感謝します。コード例とパフォーマンスに関する提案を以下に投稿します。

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Collections.Generic;


namespace ObjectSet
{
    public interface IObjectSet
    {
        /// <summary> check the existence of an object. </summary>
        /// <returns> true if object is exist, false otherwise. </returns>
        bool IsExist(object obj);

        /// <summary> if the object is not in the set, add it in. else do nothing. </summary>
        /// <returns> true if successfully added, false otherwise. </returns>
        bool Add(object obj);
    }

    public sealed class ObjectSetUsingConditionalWeakTable : IObjectSet
    {
        /// <summary> unit test on object set. </summary>
        internal static void Main() {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            ObjectSetUsingConditionalWeakTable objSet = new ObjectSetUsingConditionalWeakTable();
            for (int i = 0; i < 10000000; ++i) {
                object obj = new object();
                if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
                if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
                if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }


        public bool IsExist(object obj) {
            return objectSet.TryGetValue(obj, out tryGetValue_out0);
        }

        public bool Add(object obj) {
            if (IsExist(obj)) {
                return false;
            } else {
                objectSet.Add(obj, null);
                return true;
            }
        }

        /// <summary> internal representation of the set. (only use the key) </summary>
        private ConditionalWeakTable<object, object> objectSet = new ConditionalWeakTable<object, object>();

        /// <summary> used to fill the out parameter of ConditionalWeakTable.TryGetValue(). </summary>
        private static object tryGetValue_out0 = null;
    }

    [Obsolete("It will crash if there are too many objects and ObjectSetUsingConditionalWeakTable get a better performance.")]
    public sealed class ObjectSetUsingObjectIDGenerator : IObjectSet
    {
        /// <summary> unit test on object set. </summary>
        internal static void Main() {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            ObjectSetUsingObjectIDGenerator objSet = new ObjectSetUsingObjectIDGenerator();
            for (int i = 0; i < 10000000; ++i) {
                object obj = new object();
                if (objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
                if (!objSet.Add(obj)) { Console.WriteLine("bug!!!"); }
                if (!objSet.IsExist(obj)) { Console.WriteLine("bug!!!"); }
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
        }


        public bool IsExist(object obj) {
            bool firstTime;
            idGenerator.HasId(obj, out firstTime);
            return !firstTime;
        }

        public bool Add(object obj) {
            bool firstTime;
            idGenerator.GetId(obj, out firstTime);
            return firstTime;
        }


        /// <summary> internal representation of the set. </summary>
        private ObjectIDGenerator idGenerator = new ObjectIDGenerator();
    }
}

私のテストではObjectIDGeneratorforループで10,000,000のオブジェクト(上記のコードよりも10倍)を作成すると、オブジェクトが多すぎることを報告する例外がスローされます。

また、ベンチマークの結果は、ConditionalWeakTable実装が実装より1.8倍速いというObjectIDGeneratorことです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.