Уникальный идентификатор объекта .NET
есть ли способ получить уникальный идентификатор экземпляра?
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) уплотняет кучу (=перемещает объекты = изменяет адреса).
переполнение стека вопрос реализация по умолчанию для объекта.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
}
11 ответов:
ссылка и уникальный идентификатор объекта. Я не знаю никакого способа преобразования этого в нечто вроде строки и т. д. Значение ссылки изменится во время уплотнения (как вы видели), но каждое предыдущее значение A будет изменено на значение B, поэтому, насколько это касается безопасного кода, это все еще уникальный идентификатор.
Если объекты находятся под вашим контролем, вы можете создать отображение с помощью слабые ссылки (чтобы избежать предупреждения сборка мусора) из ссылки на идентификатор по вашему выбору (GUID, целое число, что угодно). Однако это добавило бы определенное количество накладных расходов и сложности.
.NET 4 и позже только
хорошие новости, все!
идеальный инструмент для этой работы построен в .NET 4 и называется
ConditionalWeakTable<TKey, TValue>. Этот класс:
- может использоваться для связывания произвольных данных с экземплярами управляемых объектов так же, как словарь (хотя это и не словарь)
- не зависит от адресов памяти, поэтому невосприимчив к сжатию кучи GC
- не держит объекты живы только потому, что они были введены в качестве ключей в таблице, поэтому его можно использовать не делая каждый объект в процессе жить вечно
- использует равенство ссылок для определения идентичности объекта; moveover, авторы класса не могут изменить это поведение, поэтому его можно использовать последовательно на объектах любого типа
- может быть заполнен на лету, поэтому не требует, чтобы вы вводили код внутри конструкторов объектов
проверил ObjectIDGenerator класса? Это делает то, что вы пытаетесь сделать, и то, что описывает Марк Гравелл.
ObjectIDGenerator отслеживает ранее идентифицированные объекты. При запросе идентификатора объекта ObjectIDGenerator знает, следует ли возвращать существующий идентификатор или создать и запомнить новый идентификатор.
идентификаторы уникальны для жизни экземпляра ObjectIDGenerator. Вообще, жизнь ObjectIDGenerator длится до тех пор, пока форматер, который его создал. Идентификаторы объектов имеют значение только в заданном сериализованном потоке и используются для отслеживания объектов, имеющих ссылки на другие объекты в графе сериализованных объектов.
используя хэш-таблицу, ObjectIDGenerator сохраняет идентификатор, присвоенный какому объекту. Ссылки на объекты, которые однозначно идентифицируют каждый объект, являются адресами в куче мусора, собранной во время выполнения. Значения ссылок на объекты могут изменяться во время сериализации, но таблица обновляется автоматически, поэтому информация верна.
идентификаторы объектов-это 64-разрядные числа. Выделение начинается с единицы, поэтому ноль не является допустимым идентификатором объекта. Модуль форматирования может выбрать нулевое значение для представления ссылки на объект, значение которой является нулевой ссылкой (Nothing в Visual Basic).
RuntimeHelpers.GetHashCode()может помочь ( MSDN).
вы можете разработать свою собственную вещь в секунду. Например:
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++; } }вы можете выбрать то, что вы хотели бы иметь в качестве уникального идентификатора на свой собственный, например, системы.идентификатор GUID.NewGuid () или просто целое число для быстрого доступа.
Как насчет такого метода:
установите для поля в первом объекте новое значение. Если одно и то же поле во втором объекте имеет одно и то же значение, это, вероятно, один и тот же экземпляр. В противном случае выходите как разные.
теперь установите для поля в первом объекте другое новое значение. Если одно и то же поле во втором объекте изменилось на другое значение, это определенно один и тот же экземпляр.
Не забудьте установить поле в первом объекте обратно к его оригиналу значение на выходе.
проблемы?
в Visual Studio можно создать уникальный идентификатор объекта: в окне просмотра щелкните правой кнопкой мыши переменную объекта и выберите сделать идентификатор объекта в контекстном меню.
к сожалению, это руководство шаг, и я не верю, что идентификатор можно получить с помощью кода.
вам придется назначить такой идентификатор самостоятельно, вручную-либо внутри экземпляра, либо снаружи.
для записей, связанных с базой данных, первичный ключ может быть полезен (но вы все равно можете получить дубликаты). В качестве альтернативы, либо использовать
Guid, или оставить свой собственный счетчик, выделяя с помощьюInterlocked.Increment(и сделайте его достаточно большим, чтобы он не мог переполниться).
Я знаю, что на это был дан ответ, но по крайней мере полезно отметить, что вы можете использовать:
http://msdn.microsoft.com/en-us/library/system.object.referenceequals.aspx
который не даст вам "уникальный идентификатор" напрямую, но в сочетании с WeakReferences (и hashset?) может дать вам довольно простой способ отслеживания различных инстанций.
информация, которую я здесь даю, не нова, я просто добавил Это для полноты картины.
идея этого кода довольно проста:
- объекты нуждаются в уникальном идентификаторе, которого нет по умолчанию. Вместо этого мы должны полагаться на следующую лучшую вещь, которая
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делает пару вещей. Самое главное, что он не заботится о сборщике мусора, то есть: объекты, на которые вы ссылаетесь в этой таблице, будут собраны независимо. Если вы ищете объект, он в основном работает так же, как словарь выше.любопытно, нет? В конце концов, когда объект собирается GC, он проверяет, есть ли ссылки на объект, и если есть, он собирает их. Так что если есть объект из
ConditionalWeakTable, зачем тогда будет собираться ссылочный объект?
ConditionalWeakTableиспользует небольшой трюк, который также используют некоторые другие структуры .NET: вместо хранения ссылки на объект он фактически хранит IntPtr. Поскольку это не настоящая ссылка, объект может быть собран.так, на данный момент есть 2 проблемы. Во-первых, объекты могут быть перемещены в кучу, так что мы будем использовать в качестве IntPtr? И во-вторых, как мы узнаем, что объекты имеют активный ссылка?
- объект может быть зафиксирован в куче, и его реальный указатель может быть сохранен. Когда GC попадает в объект для удаления, он открепляет его и собирает. Однако это означало бы, что мы получаем закрепленный ресурс, что не очень хорошо, если у вас много объектов (из-за проблем с фрагментацией памяти). Это, наверное, не получится.
- когда GC перемещает объект, он вызывает обратно, который затем может обновить ссылки. Это может быть, как это реализовано судя по внешним звонкам в
DependentHandle- но я считаю, что это немного более сложным.- сохраняется не указатель на сам объект, а указатель в списке всех объектов из GC. IntPtr-это либо индекс, либо указатель в этом списке. Список изменяется только тогда, когда объект меняет поколения, и в этот момент простой обратный вызов может обновить указатели. Если вы помните, как работает Mark & Sweep, это имеет больше смысла. Там нет закрепления, и удаление, как это было до. Я считаю, что это так работает в
DependentHandle.это последнее решение требует, чтобы среда выполнения не использовала повторно сегменты списка, пока они не будут явно освобождены, а также требует, чтобы все объекты извлекались вызовом среды выполнения.
если мы предположим, что они используют это решение, мы также можем решать вторую проблему. Алгоритм Mark & Sweep отслеживает, какие объекты были собраны; как только он был собран, мы знаем об этом точка. Как только объект проверяет, есть ли объект, он вызывает "Free", который удаляет указатель и запись списка. Объект действительно исчез.
одна важная вещь, чтобы отметить в этот момент, что все идет ужасно неправильно, если
ConditionalWeakTableобновляется в нескольких потоках, и если это не потокобезопасно. Результатом будет утечка памяти. Вот почему все звонки вConditionalWeakTableсделать простой замок', который гарантирует, что этого не произойдет.еще одна вещь, чтобы отметить, что очистка записи должны происходить время от времени. В то время как фактические объекты будут очищены от мусора, записей нет. Вот почему
ConditionalWeakTableтолько увеличивается в размерах. Как только он достигает определенного предела (определяется вероятностью столкновения в хэше), он запускает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 }
если вы пишете модуль в своем собственном коде для конкретного использования,majkinetor этоможет работали. Но есть некоторые проблемы.
первый официальный документ не гарантия, что
GetHashCode()возвращает уникальный идентификатор (см.
Comments