Уникальный идентификатор объекта .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
}
302   11  

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() возвращает уникальный идентификатор (см.

    Ничего не найдено.

Добавить ответ:
Отменить.