Есть ли способ получить уникальный идентификатор экземпляра?
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
}
источник
Только .NET 4 и новее
Хорошие новости всем!
Идеальный инструмент для этой работы встроен в .NET 4 и называется
ConditionalWeakTable<TKey, TValue>
. Этот класс:источник
ConditionalWeakTable
полагается на свою внутреннюю работуRuntimeHelpers.GetHashCode
иobject.ReferenceEquals
выполняет ее. Поведение такое же, как при построенииIEqualityComparer<T>
, использующего эти два метода. Если вам нужна производительность, я действительно предлагаю это сделать, посколькуConditionalWeakTable
все его операции блокируются, чтобы сделать его потокобезопасным.ConditionalWeakTable
содержит ссылку на каждый,Value
которая настолько сильна, насколько сильна ссылка в другом месте на соответствующийKey
. Объект, на которыйConditionalWeakTable
хранится единственная существующая ссылка где-либо во вселенной, автоматически перестанет существовать, когда исчезнет ключ.Проверенный в ObjectIDGenerator классе? Это делает то, что вы пытаетесь сделать, и то, что описывает Марк Гравелл.
источник
RuntimeHelpers.GetHashCode()
может помочь ( MSDN ).источник
Вы можете разработать свое собственное дело за секунду. Например:
Вы можете выбрать, какой уникальный идентификатор хотите иметь, например, System.Guid.NewGuid () или просто целое число для максимально быстрого доступа.
источник
Dispose
ошибки, потому что это предотвратит любую утилизацию.Как насчет этого метода:
Установите новое значение в поле первого объекта. Если одно и то же поле во втором объекте имеет такое же значение, вероятно, это тот же экземпляр. В противном случае выйдите по другому.
Теперь установите для поля в первом объекте другое новое значение. Если одно и то же поле во втором объекте изменилось на другое значение, это определенно один и тот же экземпляр.
Не забудьте вернуть в поле первого объекта исходное значение при выходе.
Проблемы?
источник
В Visual Studio можно создать уникальный идентификатор объекта: в окне просмотра щелкните правой кнопкой мыши переменную объекта и выберите « Создать идентификатор объекта» в контекстном меню.
К сожалению, это ручной шаг, и я не верю, что к идентификатору можно получить доступ через код.
источник
Вам придется назначить такой идентификатор вручную - внутри экземпляра или извне.
Для записей, связанных с базой данных, может быть полезен первичный ключ (но вы все равно можете получить дубликаты). В качестве альтернативы, либо используйте
Guid
, либо оставьте свой собственный счетчик, выделяя usingInterlocked.Increment
(и сделайте его достаточно большим, чтобы он не переполнялся).источник
Я знаю, что на это есть ответ, но, по крайней мере, полезно отметить, что вы можете использовать:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
Это не даст вам «уникальный идентификатор» напрямую, но в сочетании с WeakReferences (и хэшсетом?) Может дать вам довольно простой способ отслеживания различных экземпляров.
источник
Информация, которую я здесь привожу, не нова, я просто добавил ее для полноты.
Идея этого кода довольно проста:
RuntimeHelpers.GetHashCode
на получение своего рода уникального идентификатора.object.ReferenceEquals
GUID
, который по определению является уникальным.ConditionalWeakTable
.В совокупности это даст вам следующий код:
Чтобы использовать его, создайте экземпляр
UniqueIdMapper
и используйте GUID, который он возвращает для объектов.добавление
Итак, здесь происходит кое-что еще; позвольте мне немного описать
ConditionalWeakTable
.ConditionalWeakTable
делает пару вещей. Самым важным является то, что он не заботится о сборщике мусора, то есть: объекты, на которые вы ссылаетесь в этой таблице, будут собраны независимо. Если вы ищете объект, он в основном работает так же, как и приведенный выше словарь.Любопытно, нет? В конце концов, когда объект собирает сборщик мусора, он проверяет, есть ли ссылки на объект, а если есть, собирает их. Итак, если есть объект из
ConditionalWeakTable
, зачем тогда будет собираться указанный объект?ConditionalWeakTable
использует небольшой трюк, который также используют некоторые другие структуры .NET: вместо хранения ссылки на объект он фактически сохраняет IntPtr. Поскольку это не настоящая ссылка, объект можно собрать.Итак, на данный момент необходимо решить 2 проблемы. Во-первых, объекты можно перемещать в куче, так что мы будем использовать в качестве IntPtr? И во-вторых, как мы узнаем, что у объектов есть активная ссылка?
DependentHandle
но я считаю, что это немного сложнее.DependentHandle
.Это последнее решение требует, чтобы среда выполнения не использовала повторно сегменты списка до тех пор, пока они не будут явно освобождены, а также требует, чтобы все объекты извлекались с помощью вызова среды выполнения.
Если мы предположим, что они используют это решение, мы также сможем решить вторую проблему. Алгоритм Mark & Sweep отслеживает, какие объекты были собраны; как только он был собран, мы знаем об этом. Как только объект проверяет, есть ли объект, он вызывает «Free», который удаляет указатель и запись в списке. Объект действительно исчез.
Здесь следует отметить одну важную вещь: все идет ужасно неправильно, если
ConditionalWeakTable
обновление выполняется в нескольких потоках и не является потокобезопасным. Результатом будет утечка памяти. Вот почему все вызовыConditionalWeakTable
выполняются с помощью простой «блокировки», которая гарантирует, что этого не произойдет.Также следует отметить, что очистка записей должна происходить время от времени. Хотя фактические объекты будут очищены сборщиком мусора, записи - нет. Вот почему
ConditionalWeakTable
только увеличивается в размерах. Как только он достигает определенного предела (определяемого вероятностью столкновения в хэше), он запускает aResize
, который проверяет, нужно ли очистить объекты - если они это делают,free
вызывается в процессе GC, удаляяIntPtr
дескриптор.Я считаю, что это также причина, по
DependentHandle
которой не раскрывается напрямую - вы не хотите связываться с вещами и в результате получить утечку памяти. Следующим лучшим вариантом для этого являетсяWeakReference
(который также хранитIntPtr
вместо объекта), но, к сожалению, не включает аспект «зависимости».Вам остается только поиграть с механикой, чтобы увидеть зависимость в действии. Обязательно запускайте его несколько раз и смотрите результаты:
источник
ConditionalWeakTable
может быть лучше, поскольку он будет сохранять только представления для объектов, пока на них существуют ссылки. Кроме того, я бы предположил, что anInt64
может быть лучше, чем GUID, поскольку он позволяет присваивать объектам постоянный ранг . Такие вещи могут быть полезны в сценариях блокировки (например, можно избежать взаимоблокировки, если весь код, который должен будет получить несколько блокировок, делает это в некотором определенном порядке, но для того, чтобы это работало, должен быть определенный порядок).long
с; это зависит от вашего сценария - в f.ex. с распределенными системами иногда полезнее работать сGUID
s. Что касаетсяConditionalWeakTable
: вы правы;DependentHandle
проверяет живость (ПРИМЕЧАНИЕ: только когда вещь меняет размер!), что может быть здесь полезно. Тем не менее, если вам нужна производительность, блокировка может стать проблемой, так что в этом случае было бы интересно использовать это ... честно говоря, мне лично не нравится реализацияConditionalWeakTable
, что, вероятно, приводит к моему предвзятому использованию простогоDictionary
- даже хотя ты прав.ConditionalWeakTable
это работает. Тот факт, что он позволяет добавлять только элементы, заставляет меня думать, что он разработан для минимизации накладных расходов, связанных с параллелизмом, но я понятия не имею, как это работает внутри. Мне действительно любопытно, что нет простойDependentHandle
оболочки, которая не использует таблицу, так как определенно бывают моменты, когда важно убедиться, что один объект поддерживается в течение всего времени жизни другого, но у последнего объекта нет места для ссылки к первому.ConditionalWeakTable
данные не позволяют , которые хранились в таблице быть изменены. Таким образом, я думаю, что это можно безопасно реализовать, используя барьеры памяти, но не блокировки. Единственная проблемная ситуация возникнет, если два потока попытаются добавить один и тот же ключ одновременно; эту проблему можно решить, если после добавления элемента выполнить барьер памяти с помощью метода add, а затем выполнить сканирование, чтобы убедиться, что только один элемент имеет этот ключ. Если несколько элементов имеют один и тот же ключ, один из них будет идентифицирован как «первый», поэтому можно будет удалить другие.Если вы пишете модуль в своем собственном коде для использования конкретного, метод majkinetor в МОГ работал. Но есть некоторые проблемы.
Во-первых , официальный документ НЕ гарантирует, что
GetHashCode()
возвращается уникальный идентификатор (см. Object.GetHashCode Method () ):Во-вторых , предположим, что у вас очень мало объектов, так что
GetHashCode()
в большинстве случаев этот метод может быть переопределен некоторыми типами.Например, вы используете некоторый класс C, и он переопределяет,
GetHashCode()
чтобы всегда возвращать 0. Тогда каждый объект C получит один и тот же хэш-код. К сожалению,Dictionary
,HashTable
и некоторые другие ассоциативные контейнеры будут использовать этот метод:Итак, у этого подхода есть большие ограничения.
И даже более того , что, если вы хотите создать библиотеку общего назначения? Вы не только не можете изменять исходный код используемых классов, но и их поведение также непредсказуемо.
Я ценю, что Джон и Саймон опубликовали свои ответы, и я опубликую ниже пример кода и предложения по производительности.
В моем тесте
ObjectIDGenerator
вызывается исключение, чтобы пожаловаться на то, что при создании 10 000 000 объектов (в 10 раз больше, чем в приведенном выше коде) вfor
цикле имеется слишком много объектов .Кроме того, результат теста показывает, что
ConditionalWeakTable
реализация выполняется в 1,8 раза быстрее, чемObjectIDGenerator
реализация.источник