Уникальный идентификатор объекта .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 отладчика. Если бы я контролировал объекты, добавление собственных уникальных идентификаторов было бы тривиальным делом.

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

Если задействованные объекты находятся под вашим контролем, вы можете создать сопоставление с использованием слабых ссылок (во избежание предотвращения сборки мусора) из ссылки на идентификатор по вашему выбору (GUID, целое число и т. Д.). Однако это добавило бы определенных накладных расходов и сложности.


1
Я предполагаю, что для поиска вам придется перебирать все отслеживаемые вами ссылки: WeakReference на один и тот же объект не равны друг другу, поэтому вы действительно не можете ничего сделать.
— Роман Старков

1
В том, чтобы каждому объекту был назначен уникальный 64-битный идентификатор, может быть некоторая полезность, особенно если такие идентификаторы выдавались последовательно. Я не уверен, что полезность оправдает затраты, но такая вещь может быть полезна, если сравнивать два различных неизменяемых объекта и находить их равными; если по возможности заменить ссылку на более новую ссылку на более старую, можно избежать множества избыточных ссылок на идентичные, но разные объекты.
— supercat

1
"Идентификатор." Я не думаю, что это слово означает то, что вы думаете.
— Слипп Д. Томпсон

5
@ SlippD.Thompson: Нет, это по-прежнему соотношение один к одному. Есть только одно ссылочное значение, которое относится к любому заданному объекту. Это значение может появляться в памяти много раз (например, как значение нескольких переменных), но это все равно одно значение. Это похоже на домашний адрес: я могу записать свой домашний адрес на нескольких листах бумаги, но это все равно идентификатор моего дома. Любые два неидентичных ссылочных значения должны относиться к разным объектам - по крайней мере, в C #.
— Джон Скит,

1
@supercat: Я думаю, что мы можем по-разному понимать «инкапсулируемые личности» - но я думаю, что мы также, вероятно, никому не поможем продвинуться дальше, чем мы уже сделали :) Это одна из тем, которую мы должны обсудить подробно, если мы когда-либо встречаемся лично ...
— Джон Скит

72

Только .NET 4 и новее

Хорошие новости всем!

Идеальный инструмент для этой работы встроен в .NET 4 и называется ConditionalWeakTable<TKey, TValue>. Этот класс:

  • может быть использовано , чтобы связать произвольные данные с управляемыми экземплярами объекта так же, как словарь (хотя это не словарь)
  • не зависит от адресов памяти, поэтому невосприимчив к GC, уплотняющему кучу
  • не поддерживает объекты в живых только потому, что они были введены в качестве ключей в таблицу, поэтому его можно использовать, не заставляя каждый объект в вашем процессе жить вечно
  • использует ссылочное равенство для определения идентичности объекта; moveover, авторы классов не могут изменять это поведение, поэтому его можно последовательно использовать для объектов любого типа
  • можно заполнять на лету, поэтому не требуется вводить код внутри конструкторов объектов

5
Просто для полноты: ConditionalWeakTableполагается на свою внутреннюю работу RuntimeHelpers.GetHashCodeи object.ReferenceEqualsвыполняет ее. Поведение такое же, как при построении IEqualityComparer<T>, использующего эти два метода. Если вам нужна производительность, я действительно предлагаю это сделать, поскольку ConditionalWeakTableвсе его операции блокируются, чтобы сделать его потокобезопасным.
— atlaste 07

1
@StefandeBruijn: A ConditionalWeakTableсодержит ссылку на каждый, Valueкоторая настолько сильна, насколько сильна ссылка в другом месте на соответствующий Key. Объект, на который ConditionalWeakTableхранится единственная существующая ссылка где-либо во вселенной, автоматически перестанет существовать, когда исчезнет ключ.
— supercat 07

41

Проверенный в ObjectIDGenerator классе? Это делает то, что вы пытаетесь сделать, и то, что описывает Марк Гравелл.

ObjectIDGenerator отслеживает ранее идентифицированные объекты. Когда вы запрашиваете идентификатор объекта, ObjectIDGenerator знает, возвращать ли существующий идентификатор или сгенерировать и запомнить новый идентификатор.

Идентификаторы уникальны на время существования экземпляра ObjectIDGenerator. Обычно жизнь ObjectIDGenerator длится столько же, сколько Formatter, создавший его. Идентификаторы объектов имеют значение только в данном сериализованном потоке и используются для отслеживания того, какие объекты имеют ссылки на другие в пределах сериализованного графа объектов.

Используя хеш-таблицу, ObjectIDGenerator сохраняет идентификатор, присвоенный какому объекту. Ссылки на объекты, которые однозначно идентифицируют каждый объект, являются адресами в куче со сборкой мусора во время выполнения. Значения ссылок на объекты могут изменяться во время сериализации, но таблица обновляется автоматически, поэтому информация верна.

Идентификаторы объектов - это 64-битные числа. Распределение начинается с единицы, поэтому ноль никогда не является допустимым идентификатором объекта. Средство форматирования может выбрать нулевое значение для представления ссылки на объект, значение которой является пустой ссылкой (Nothing в Visual Basic).


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

Вероятно, лучшее решение, когда требуются распечатываемые уникальные идентификаторы.
— Роман Старков

ObjectIDGenerator также не реализован на телефоне.
— Энтони Визер,

Я не совсем понимаю, что делает ObjectIDGenerator, но, похоже, он работает, даже когда он использует RuntimeHelpers.GetHashCode. Я тестировал оба, и только RuntimeHelpers.GetHashCode в моем случае не работает.
— Daniel Bişar

+1 - Работает неплохо (по крайней мере, на рабочем столе).
— Hot Licks

37

RuntimeHelpers.GetHashCode()может помочь ( MSDN ).


2
Это может помочь, но с затратами - IIRC с использованием базового объекта. GetHashCode () должен выделить блок синхронизации, который не является бесплатным. Хорошая идея - +1 от меня.
— Джон Скит,

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

1
Вы можете использовать GCHandle, если вам не нужно их слишком много (см. Ниже).
— Антон Тихий

42
В книге по .NET, написанной очень уважаемым автором, говорится, что RuntimeHelpers.GetHashCode () создает код, уникальный в пределах домена приложения, и что Microsoft могла бы назвать метод GetUniqueObjectID. Это просто неправильно. При тестировании я обнаружил, что обычно получаю дубликат к тому времени, когда я создаю 10 000 экземпляров объекта (WinForms TextBox), и никогда не мог пройти мимо 30 000. Код, основанный на предполагаемой уникальности, вызывал периодические сбои в производственной системе после создания не более 1/10 этого количества объектов.
— Ян Хеттич

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
Это не совсем работает, поскольку словарь использует равенство вместо идентичности, сворачивая объекты, которые возвращают те же значения для объекта. Equals
— Энтони Визер

1
Однако это сохранит объект в живых.
— Мартин Лоттеринг

1
@MartinLottering, что, если он использует ConditionalWeakTable <object, idType>?
— Деметрис Лептос

7

Как насчет этого метода:

Установите новое значение в поле первого объекта. Если одно и то же поле во втором объекте имеет такое же значение, вероятно, это тот же экземпляр. В противном случае выйдите по другому.

Теперь установите для поля в первом объекте другое новое значение. Если одно и то же поле во втором объекте изменилось на другое значение, это определенно один и тот же экземпляр.

Не забудьте вернуть в поле первого объекта исходное значение при выходе.

Проблемы?


4

В Visual Studio можно создать уникальный идентификатор объекта: в окне просмотра щелкните правой кнопкой мыши переменную объекта и выберите « Создать идентификатор объекта» в контекстном меню.

К сожалению, это ручной шаг, и я не верю, что к идентификатору можно получить доступ через код.


В каких версиях Visual Studio есть эта функция? Например, экспресс-версии?
— Питер Мортенсен

3

Вам придется назначить такой идентификатор вручную - внутри экземпляра или извне.

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


2

Я знаю, что на это есть ответ, но, по крайней мере, полезно отметить, что вы можете использовать:

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

Это не даст вам «уникальный идентификатор» напрямую, но в сочетании с WeakReferences (и хэшсетом?) Может дать вам довольно простой способ отслеживания различных экземпляров.


1

Информация, которую я здесь привожу, не нова, я просто добавил ее для полноты.

Идея этого кода довольно проста:

  • Объектам нужен уникальный идентификатор, которого нет по умолчанию. Вместо этого мы должны полагаться на следующую лучшую вещь, а именно RuntimeHelpers.GetHashCodeна получение своего рода уникального идентификатора.
  • Это означает, что для проверки уникальности нам нужно использовать object.ReferenceEquals
  • Однако мы все равно хотели бы иметь уникальный идентификатор, поэтому я добавил 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. Поскольку это не настоящая ссылка, объект можно собрать.

Итак, на данный момент необходимо решить 2 проблемы. Во-первых, объекты можно перемещать в куче, так что мы будем использовать в качестве IntPtr? И во-вторых, как мы узнаем, что у объектов есть активная ссылка?

  • Объект можно закрепить в куче и сохранить его реальный указатель. Когда сборщик мусора попадает в объект для удаления, он открепляет его и забирает. Однако это будет означать, что мы получим закрепленный ресурс, что не является хорошей идеей, если у вас много объектов (из-за проблем фрагментации памяти). Вероятно, это не так.
  • Когда GC перемещает объект, он выполняет обратный вызов, который затем может обновить ссылки. Судя по внешним вызовам, это может быть именно так, DependentHandleно я считаю, что это немного сложнее.
  • Сохраняется не указатель на сам объект, а указатель в списке всех объектов из GC. IntPtr - это либо индекс, либо указатель в этом списке. Список изменяется только тогда, когда объект меняет поколения, и в этот момент простой обратный вызов может обновить указатели. Если вы помните, как работает Mark & ​​Sweep, это имеет больше смысла. Прикрепления нет, а удаление как было раньше. Я считаю, что вот как это работает DependentHandle.

Это последнее решение требует, чтобы среда выполнения не использовала повторно сегменты списка до тех пор, пока они не будут явно освобождены, а также требует, чтобы все объекты извлекались с помощью вызова среды выполнения.

Если мы предположим, что они используют это решение, мы также сможем решить вторую проблему. Алгоритм Mark & ​​Sweep отслеживает, какие объекты были собраны; как только он был собран, мы знаем об этом. Как только объект проверяет, есть ли объект, он вызывает «Free», который удаляет указатель и запись в списке. Объект действительно исчез.

Здесь следует отметить одну важную вещь: все идет ужасно неправильно, если ConditionalWeakTableобновление выполняется в нескольких потоках и не является потокобезопасным. Результатом будет утечка памяти. Вот почему все вызовы ConditionalWeakTableвыполняются с помощью простой «блокировки», которая гарантирует, что этого не произойдет.

Также следует отметить, что очистка записей должна происходить время от времени. Хотя фактические объекты будут очищены сборщиком мусора, записи - нет. Вот почему 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
A ConditionalWeakTableможет быть лучше, поскольку он будет сохранять только представления для объектов, пока на них существуют ссылки. Кроме того, я бы предположил, что an Int64может быть лучше, чем GUID, поскольку он позволяет присваивать объектам постоянный ранг . Такие вещи могут быть полезны в сценариях блокировки (например, можно избежать взаимоблокировки, если весь код, который должен будет получить несколько блокировок, делает это в некотором определенном порядке, но для того, чтобы это работало, должен быть определенный порядок).
— supercat 07

@supercat Конечно насчет longс; это зависит от вашего сценария - в f.ex. с распределенными системами иногда полезнее работать с GUIDs. Что касается ConditionalWeakTable: вы правы; DependentHandleпроверяет живость (ПРИМЕЧАНИЕ: только когда вещь меняет размер!), что может быть здесь полезно. Тем не менее, если вам нужна производительность, блокировка может стать проблемой, так что в этом случае было бы интересно использовать это ... честно говоря, мне лично не нравится реализация ConditionalWeakTable, что, вероятно, приводит к моему предвзятому использованию простого Dictionary- даже хотя ты прав.
— atlaste 08

Мне давно интересно, как ConditionalWeakTableэто работает. Тот факт, что он позволяет добавлять только элементы, заставляет меня думать, что он разработан для минимизации накладных расходов, связанных с параллелизмом, но я понятия не имею, как это работает внутри. Мне действительно любопытно, что нет простой DependentHandleоболочки, которая не использует таблицу, так как определенно бывают моменты, когда важно убедиться, что один объект поддерживается в течение всего времени жизни другого, но у последнего объекта нет места для ссылки к первому.
— supercat 08

@supercat Я отправлю дополнение о том, как я думаю, что это работает.
— atlaste

Эти ConditionalWeakTableданные не позволяют , которые хранились в таблице быть изменены. Таким образом, я думаю, что это можно безопасно реализовать, используя барьеры памяти, но не блокировки. Единственная проблемная ситуация возникнет, если два потока попытаются добавить один и тот же ключ одновременно; эту проблему можно решить, если после добавления элемента выполнить барьер памяти с помощью метода add, а затем выполнить сканирование, чтобы убедиться, что только один элемент имеет этот ключ. Если несколько элементов имеют один и тот же ключ, один из них будет идентифицирован как «первый», поэтому можно будет удалить другие.
— supercat 09

0

Если вы пишете модуль в своем собственном коде для использования конкретного, метод majkinetor в МОГ работал. Но есть некоторые проблемы.

Во-первых , официальный документ НЕ гарантирует, что GetHashCode()возвращается уникальный идентификатор (см. Object.GetHashCode Method () ):

Не следует предполагать, что одинаковые хэш-коды подразумевают равенство объектов.

Во-вторых , предположим, что у вас очень мало объектов, так что GetHashCode()в большинстве случаев этот метод может быть переопределен некоторыми типами.
Например, вы используете некоторый класс C, и он переопределяет, GetHashCode()чтобы всегда возвращать 0. Тогда каждый объект C получит один и тот же хэш-код. К сожалению, Dictionary, HashTableи некоторые другие ассоциативные контейнеры будут использовать этот метод:

Хэш-код - это числовое значение, которое используется для вставки и идентификации объекта в коллекции на основе хешей, такой как класс Dictionary <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.