.NET унікальний ідентифікатор об'єкта


118

Чи є спосіб отримати унікальний ідентифікатор примірника?

GetHashCode()однаково для двох посилань, що вказують на один і той же екземпляр. Однак два різні екземпляри можуть (досить легко) отримати один і той же хеш-код:

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

Я пишу надбудову для налагодження, і мені потрібно отримати якийсь ідентифікатор для довідки, який є унікальним під час виконання програми.

Мені вже вдалося отримати внутрішню АДРЕСУ екземпляра, яка є унікальною, поки збирач сміття (GC) не ущільнить купу (= переміщає об'єкти = змінює адреси).

Питання про переповнення стека реалізацією за замовчуванням для Object.GetHashCode () може бути пов'язано.

Об'єкти не знаходяться під моїм контролем, оскільки я отримую доступ до об'єктів у програмі налагодження за допомогою API налагодження. Якби я контролював об’єкти, додавання власних унікальних ідентифікаторів було б тривіальним.

Я хотів, щоб унікальний ідентифікатор для побудови об'єкта хеш-ідентифікатора -> міг шукати вже бачені об’єкти. Поки що я вирішив це так:

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, наскільки що стосується безпечного коду, це все ще унікальний ідентифікатор.

Якщо об’єкти, що займаються, перебувають під вашим контролем, ви можете створити відображення за допомогою слабких посилань (щоб уникнути заборони вивезення сміття) із посилання на ідентифікатор, який ви обрали (GUID, ціле число та інше). Однак це додало б певної суми витрат та складності.


1
Я думаю, що для пошуку, вам доведеться повторити всі відслідковувані вами посилання: WeakReference до одного і того ж об'єкта не дорівнюють один одному, тому ви не можете реально зробити багато іншого.
Роман Старков

1
Можливо, було б корисно, якщо кожному об'єкту було присвоєно унікальний 64-розрядний ідентифікатор, особливо якщо такі ідентифікатори видавалися послідовно. Я не впевнений, що корисність виправдовувала б вартість, але така річ може бути корисною, якщо порівнювати два чіткі непорушні об'єкти і вважати їх рівними; якщо один, коли можливо, замінює посилання на новіший із посиланням на старіший, можна уникнути наявності багатьох зайвих посилань на однакові, але чіткі об'єкти.
supercat

1
"Ідентифікатор". Я не думаю, що це слово означає те, що ти думаєш, що воно означає.
Сліпп Д. Томпсон

5
@ SlippD.Thompson: Ні, це все ще відношення 1 до 1. Існує лише одне опорне значення, яке посилається на будь-який даний об'єкт. Це значення може з’являтися багато разів у пам'яті (наприклад, як значення декількох змінних), але все одно це одне значення. Це як домашня адреса: я можу записати свою домашню адресу на декількох аркушах паперу, але це все ще ідентифікатор для мого будинку. Будь-які два невідповідні опорні значення повинні стосуватися різних об'єктів - принаймні у C #.
Джон Скіт

1
@supercat: Я думаю, що ми можемо відрізнятися в нашому розумінні "ідентичності, яка інкапсульована" - але я думаю, що ми також, мабуть, нікому не допомагаємо йти далі, ніж ми вже маємо :) Лише одна з тем, про яку слід довго обговорити, якщо ми коли-небудь зустрічаємось особисто ...
Джон Скіт

72

.NET 4 та новіші версії

Гарні новини, всі!

Ідеальний інструмент для цієї роботи вбудований в .NET 4 і його називають ConditionalWeakTable<TKey, TValue>. Цей клас:

  • може бути використано , щоб пов'язати довільні дані з керованими екземплярами об'єкта так само, як словник (хоча це не словник,)
  • не залежить від адрес пам'яті, тому не захищений від GC, що ущільнює купу
  • не зберігає об’єкти в живих лише тому, що вони були введені як ключі в таблицю, тому їх можна використовувати, не роблячи кожен об'єкт у вашому процесі живим назавжди
  • використовує опорну рівність для визначення ідентичності об'єкта; З іншого боку, автори класів не можуть змінювати цю поведінку, тому вона може послідовно використовуватись на об'єктах будь-якого типу
  • може бути заповнений на льоту, тому не потрібно вводити код всередину конструкторів об'єктів

5
Просто для повноти: ConditionalWeakTableпокладається на RuntimeHelpers.GetHashCodeта object.ReferenceEqualsвиконує свою внутрішню роботу. Поведінка така ж, як і побудова системи, IEqualityComparer<T>яка використовує ці два методи. Якщо вам потрібна продуктивність, я фактично пропоную це зробити, оскільки ConditionalWeakTableмає фіксацію навколо всіх своїх операцій, щоб зробити її потоком безпечним.
атласуйте

1
@StefandeBruijn: ConditionalWeakTableПосилання на кожного, Valueяка є настільки ж сильною, як посилання, що міститься в іншому місці на відповідну Key. Об'єкт, на який міститься ConditionalWeakTableєдине діюче посилання в будь-якій точці Всесвіту, автоматично припинить своє існування, коли ключ з'явиться.
supercat

41

Перевірили клас ObjectIDGenerator ? Це робить те, що ви намагаєтеся зробити, і те, що описує Марк Гравелл.

ObjectIDGenerator відстежує попередньо ідентифіковані об'єкти. Коли ви запитуєте ідентифікатор об'єкта, ObjectIDGenerator знає, чи потрібно повертати існуючий ідентифікатор, або генерувати та запам'ятовувати новий ідентифікатор.

Ідентифікатори є унікальними для життя екземпляра ObjectIDGenerator. Як правило, життя ObjectIDGenerator триває стільки ж, скільки і Форматтер, який його створив. Ідентифікатори об'єктів мають значення лише в заданому серіалізованому потоці і використовуються для відстеження, які об'єкти мають посилання на інших в межах серіалізованого графіка об'єктів.

Використовуючи хеш-таблицю, ObjectIDGenerator зберігає, який ідентифікатор присвоєно об’єкту. Посилання на об'єкти, які однозначно ідентифікують кожен об'єкт, - це адреси в купі зібраного сміття під час виконання. Опорні значення об'єкта можуть змінюватися під час серіалізації, але таблиця оновлюється автоматично, щоб інформація була правильною.

Ідентифікатори об'єктів - це 64-бітні числа. Виділення починається з одиниці, тому нуль ніколи не є дійсним ідентифікатором об’єкта. Форматор може вибрати нульове значення для відображення посилання на об'єкт, значення якого є нульовою посиланням (Nothing in Visual Basic).


5
Reflector повідомляє мені, що ObjectIDGenerator є хешбелом, спираючись на типову реалізацію GetHashCode (тобто він не використовує перевантаження користувачів).
Антон Тихий

Мабуть, найкраще рішення, коли потрібні унікальні ідентифікатори для друку.
Роман Старков

ObjectIDGenerator також не реалізований на телефоні.
Ентоні Візер

Я не точно розумію, чим займається ObjectIDGenerator, але, здається, працює, навіть коли він використовує RuntimeHelpers.GetHashCode. Я перевірив і те, і лише RuntimeHelpers.GetHashCode не вдається в моєму випадку.
Даніель Бішар

+1 - працює досить гладко (як мінімум на робочому столі).
Гарячі лизання

37

RuntimeHelpers.GetHashCode()може допомогти ( MSDN ).


2
Це може допомогти, але з вартістю - IIRC, використовуючи базовий об'єкт.GetHashCode () повинен виділити блок синхронізації, який не є безкоштовним. Хороша ідея, хоча - +1 від мене.
Джон Скіт

Дякую, я не знав цього методу. Однак він також не створює унікальний хеш-код (поводиться точно так само, як зразок коду у питанні). Буде корисно, хоча якщо користувач переорієнтує хеш-код, щоб викликати версію за замовчуванням.
Мартін Конічек

1
Ви можете використовувати GCHandle, якщо їх не потрібно занадто багато (див. Нижче).
Антон Тихий

42
У книзі про .NET високоповажного автора зазначено, що RuntimeHelpers.GetHashCode () створить код, унікальний у AppDomain, і що Microsoft могла б назвати метод GetUniqueObjectID. Це просто неправильно. Під час тестування я виявив, що зазвичай отримував дублікат до моменту створення 10 000 екземпляра об'єкта (WinForms TextBox), і ніколи не міг отримати понад 30 000. Код, покладаючись на передбачувану унікальність, спричинив переривчасті збої у виробничій системі після створення не більше 1/10 від багатьох об'єктів.
Jan Hettich

3
@supercat: Ага - щойно знайшли деякі докази, починаючи з 2003 року, що було з .NET 1.0 та 1.1. Схоже, вони планували змінити на .NET 2: blogs.msdn.com/b/brada/archive/2003/09/30/50396.aspx
Джон Скіт

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 (), або просто ціле число для швидкого доступу.


2
Не допоможуть, якщо для цього вам потрібні Disposeпомилки, оскільки це запобіжить будь-якому виду утилізації.
Роман Старков

1
Це не зовсім працює, оскільки словник використовує рівність замість тотожності, згортаючи об’єкти, які повертають однакові значення для об’єкта. Рівні
Ентоні Візер

1
Це все ж збереже живий об’єкт.
Мартін Лоттерінг

1
@MartinLottering Що робити, якщо він використовує ConditionalWeakTable <об’єкт, idType>?
Деметрис Лептос

7

Як щодо цього методу:

Встановіть поле першого об'єкта на нове значення. Якщо одне і те ж поле у ​​другому об'єкті має те саме значення, це, мабуть, той самий екземпляр. В іншому випадку вийдіть як інакше.

Тепер встановіть для поля в першому об’єкті інше нове значення. Якщо те саме поле у ​​другому об'єкті змінилося на інше значення, це, безумовно, той самий екземпляр.

Не забудьте встановити поле в першому об’єкті назад до його початкового значення при виході.

Проблеми?


4

У Visual Studio можна зробити унікальний ідентифікатор об'єкта: у вікні перегляду правою кнопкою миші клацніть змінну об'єкта та виберіть З контекстного меню оберіть Ідентифікатор об’єкта .

На жаль, це крок вручну, і я не вірю, що до ідентифікатора можна отримати доступ за допомогою коду.


Які версії Visual Studio мають цю функцію? Наприклад, версії Express?
Пітер Мортенсен

3

Вам слід було б призначити такий ідентифікатор самостійно, вручну - або всередині екземпляра, або зовні.

Для записів, пов’язаних із базою даних, первинний ключ може бути корисним (але ви все одно можете отримати дублікати). Крім того, або використовуйте a Guid, або зберігайте власний лічильник, виділяючи його Interlocked.Increment(і зробіть його достатньо великим, щоб він, швидше за все, не переповнювався).


2

Я знаю, що на це відповіли, але принаймні корисно зазначити, що ви можете використовувати:

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

Що не дасть вам "унікальний ідентифікатор" безпосередньо, але в поєднанні з WeakReferences (і хеш-версією?) Може дати вам досить простий спосіб відстеження різних примірників.


1

Інформація, яку я даю тут, не нова, я просто додав це для повноти.

Ідея цього коду досить проста:

  • Об'єктам потрібен унікальний ідентифікатор, якого немає за замовчуванням. Натомість ми маємо покладатися на наступне найкраще, що полягає RuntimeHelpers.GetHashCodeв тому, щоб отримати нам свого роду унікальний ідентифікатор
  • Щоб перевірити унікальність, це означає, що нам потрібно використовувати object.ReferenceEquals
  • Однак ми все-таки хотіли б мати унікальний ідентифікатор, тому я додав a 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робить пару речей. Найголовніше, що це не хвилює збиральника сміття, тобто об’єкти, на які ви посилаєтесь у цій таблиці, збиратимуться незалежно. Якщо ви шукаєте об’єкт, він в основному працює так само, як і словник, наведений вище.

Цікаве ні? Зрештою, коли об’єкт збирається ГК, він перевіряє, чи є посилання на об’єкт, і якщо вони є, він збирає їх. Отже, якщо є об'єкт із ConditionalWeakTable, чому тоді буде зібраний посилальний об'єкт?

ConditionalWeakTableвикористовує невеликий трюк, який використовують і деякі інші .NET структури: замість того, щоб зберігати посилання на об’єкт, він фактично зберігає IntPtr. Оскільки це не справжня довідка, об’єкт можна зібрати.

Отже, на даний момент є дві проблеми, які потрібно вирішити. По-перше, об’єкти можна переміщувати по купі, так що ж ми будемо використовувати як IntPtr? По-друге, як ми знаємо, що об’єкти мають активну довідку?

  • Об'єкт можна закріпити на купі, а його реальний покажчик може бути збережений. Коли GC потрапляє на об’єкт для видалення, він відкручує його та збирає. Однак це означає, що ми отримуємо закріплений ресурс, що не дуже добре, якщо у вас багато об’єктів (через проблеми з фрагментацією пам'яті). Це, мабуть, не так, як це працює.
  • Коли GC переміщує об'єкт, він передзвонює, який потім може оновити посилання. Це може бути, як це реалізується, судячи з зовнішніх дзвінків, DependentHandle- але я вважаю, що це дещо складніше.
  • Зберігається не вказівник на сам об’єкт, а вказівник у списку всіх об'єктів із GC. IntPtr - це або індекс, або вказівник у цьому списку. Список змінюється лише тоді, коли об'єкт змінює покоління, і тоді простий зворотний виклик може оновити покажчики. Якщо ви пам’ятаєте, як працює Mark & ​​Sweep, це має більше сенсу. Немає фіксації, а видалення відбувається як раніше. Я вважаю, що так це працює DependentHandle.

Це останнє рішення вимагає, щоб час виконання не використовував повторно списки, поки вони не будуть явно звільнені, а також потрібно, щоб усі об'єкти були отримані за допомогою виклику до виконання.

Якщо припустити, що вони використовують це рішення, ми також можемо вирішити другу проблему. Алгоритм Позначення і зачистка відстежує, які об’єкти були зібрані; як тільки він буде зібраний, ми знаємо на даний момент. Як тільки об’єкт перевіряє, чи є об'єкт, він викликає "Безкоштовно", що видаляє вказівник та запис списку. Об’єкта справді немає.

Одне важливе, що слід зазначити в цьому моменті, - це те, що вони йдуть жахливо неправильно, якщо ConditionalWeakTableвони оновлюються в декількох потоках і якщо це не є безпечним для потоків. Результатом буде витік пам'яті. Ось чому всі дзвінки ConditionalWeakTableвиконувати простий "замок", який забезпечує те, що цього не відбувається.

Ще одна річ, що слід зазначити, що чистка записів повинна відбуватися раз у раз. Хоча фактичні об’єкти будуть очищені GC, записи не є. Ось чому ConditionalWeakTableтільки збільшується в розмірах. Як тільки він досягає певної межі (визначається випадковістю зіткнення в хеші), він спрацьовує a Resize, який перевіряє, чи потрібно об'єкти очищати - якщо вони freeє, викликається в процесі GC, видаляючи IntPtrручку.

Я вважаю, що це також причина DependentHandle, що не піддається впливу - ви не хочете возитися з речами і в результаті отримувати витік пам'яті. Наступне найкраще для цього - це WeakReference(який також зберігає IntPtrзамість об'єкта) - але, на жаль, не включає аспект "залежності".

Залишилося вам пограти з механікою, щоб ви могли бачити залежність в дії. Не забудьте запустити його кілька разів і переглянути результати:

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може бути краще, оскільки воно буде зберігати уявлення лише про об'єкти, тоді як на них існують посилання. Крім того, я б припустив, що це Int64може бути кращим, ніж GUID, оскільки це дозволить об'єктам присвоїти стійке звання . Такі речі можуть бути корисними при сценаріях блокування (наприклад, можна уникнути тупикової ситуації, якщо один увесь код, який потребує придбання декількох блокувань, робить це в певному певному порядку, але для цього має бути визначений порядок).
supercat

@supercat Безумовно про longs; це залежить від вашого сценарію - в f.ex. розподілених систем Іноді корисніше працювати з GUIDs. Щодо ConditionalWeakTable: ви праві; DependentHandleперевірки на придатність (ПРИМІТКА: лише тоді, коли річ змінить розмір!), що може бути корисним тут. Тим не менш, якщо вам потрібна продуктивність, блокування може стати проблемою там, тож у цьому випадку це може бути цікаво використати ... якщо чесно, мені особисто не подобається реалізація ConditionalWeakTable, що, ймовірно, призводить до моєї упередженості використання простого Dictionary- навіть хоч ти прав.
atlaste

Мені давно цікаво, як ConditionalWeakTableнасправді працює. Той факт, що він дозволяє лише додавати елементи, змушує мене думати, що він розроблений для мінімізації накладних витрат, пов'язаних з паралельною валютою, але я не маю уявлення, як це працює всередині країни. Мені цікаво, що немає простої DependentHandleобгортки, яка не використовує таблицю, оскільки, безумовно, є випадки, коли важливо забезпечити збереження одного об’єкта протягом життя іншого, але останній об'єкт не має місця для посилання до першого.
supercat

@supercat Я опублікую додаток про те, як я думаю, що це працює.
атлас

Ці ConditionalWeakTableдані не дозволяють , які зберігалися в таблиці бути змінені. Як таке, я думаю, що це могло б бути реалізовано безпечно, використовуючи бар'єри пам'яті, але не блокування. Єдиною проблемною ситуацією було б, якби два потоки намагалися одночасно додати один і той же ключ; це можна було б вирішити, якщо метод "add" виконає бар'єр пам'яті після додавання елемента, а потім сканує, щоб переконатися, що саме в одному елементі є цей ключ. Якщо кілька елементів мають один і той же ключ, один з них буде ідентифікований як "перший", тому можна буде усунути інші.
supercat

0

Якщо ви пишете модуль у власному коді для певного використання, метод majkinetor MIGHT працював. Але є деякі проблеми.

По-перше , офіційний документ НЕ гарантує GetHashCode()повернення унікального ідентифікатора (див. Метод Object.GetHashCode () ):

Не слід вважати, що рівні хеш-коди означають рівність об'єкта.

По-друге , припустимо, що у вас дуже мала кількість об'єктів, так щоGetHashCode() вони працюватимуть у більшості випадків. Цей метод може бути замінений деякими типами.
Наприклад, ви використовуєте деякий клас C і він переосмислює GetHashCode()завжди повернення 0. Тоді кожен об'єкт C отримає однаковий хеш-код. На жаль, Dictionary, HashTableі деякі інші асоціативні контейнери будуть використовувати цей метод:

Хеш-код - це числове значення, яке використовується для вставки та ідентифікації об'єкта в колекції на основі хешів, таких як клас Словник <TKey, TValue>, клас Hashtable або тип, отриманий з класу DictionaryBase. Метод GetHashCode надає цей хеш-код для алгоритмів, які потребують швидкої перевірки рівності об'єктів.

Отже, такий підхід має великі обмеження.

І навіть більше , що робити, якщо ви хочете побудувати бібліотеку загального призначення? Ви не тільки не можете змінити вихідний код використовуваних класів, але і їх поведінку також непередбачувано.

Я ціную це Джон і Саймон опублікували свої відповіді, і я опублікую приклад коду та пропозицію щодо продуктивності нижче.

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

У моєму тесті ObjectIDGenerator заповіт видасть виняток, щоб поскаржитися на те, що при створенні 10 000 000 об'єктів (10 разів ніж у коді вище) в forциклі є занадто багато об'єктів .

Крім того, результатом є те, що ConditionalWeakTableреалізація на 1,8 рази швидша, ніж ObjectIDGeneratorреалізація.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.