views:

1897

answers:

6

Is there any way of getting an unique identifier of an instance? GetHashCode() is same for 2 references pointing to the same instance. However, 2 different instances can (quite early) get same hash code:

Hashtable hashCodesSeen = new Hashtable();  
LinkedList<object> l = new LinkedList<object>();  
int n = 0;  
while (true)  
{  
    object o = new object();  
    // remember object 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);  
}

I'm writing a debugging addin and I need to get some kind of ID for a reference which is unique during the run of the program.

I already managed to get internal ADDRESS of the instance, which is unique until the GC compacts the heap (= moves the objects = changes the addresses).

This thread might be related. Thanks for any advice.

[edit] Hi, thanks everyone for responses. I should have stated more clearly that the objects are not under my control as I am accessing objects in a program being debugged using the debugger API. If I was in control of the objects, adding my own unique identifiers would be trivial.
I wanted the unique ID for building a hashtable ID -> object, to be able to lookup already seen objects. For now I solved it like this:

build a hashtable: 'hashCode' -> (list of objects with hash code == 'hashCode')
find if object seen(o) {
    candidates = hashtable[o.GetHashCode()] // objects with same hashCode
    if no candidates, the object is new
    if some candidates, compare their addresses to o.Address
      if no address equal (the hash code was just a coincidence) -> o is new
      if some address equal, o already seen
}
+7  A: 

The reference is the unique identifier for the object. I don't know of any way of converting this into anything like a string etc. The value of the reference will change during compaction (as you've seen), but every previous value A will be changed to value B, so as far as safe code is concerned it's still a unique ID.

If the objects involved are under your control, you could create a mapping using weak references (to avoid preventing garbage collection) from a reference to an ID of your choosing (GUID, integer, whatever). That would add a certain amount of overhead and complexity, however.

Jon Skeet
I guess for lookups you'd have to iterate over all the references you track: WeakReference to the same object are not equal to each other, so you can't really do much else.
romkyns
+4  A: 

Is RuntimeHelpers.GetHashCode() of any help?

Anton Gogolev
That may well help, but with a cost - IIRC, using the base object.GetHashCode() needs to allocate a sync block, which isn't free. Nice idea though - +1 from me.
Jon Skeet
Thanks, I didn't know this method. However, it does not produce unique hash code either (behaves exactly the same as the sample code in the question). Will be useful though if the user overrides hash code, to call the default version.
Martin Konicek
You can use GCHandle if you don't need too many of them (see below).
Anton Tykhyy
+2  A: 

You would have to assign such an identifier yourself, manually - either inside the instance, or externally.

For records related to a database, the primary key may be useful (but you can still get duplicates). Alternatively, either use a Guid, or keep your own counter, allocating using Interlocked.Increment (and make it large enough that it isn't likely to overflow).

Marc Gravell
+4  A: 

Checked out the ObjectIDGenerator class? This does what you're attempting to do, and what Marc Gravell describes.

The ObjectIDGenerator keeps track of previously identified objects. When you ask for the ID of an object, the ObjectIDGenerator knows whether to return the existing ID, or generate and remember a new ID.

The IDs are unique for the life of the ObjectIDGenerator instance. Generally, a ObjectIDGenerator life lasts as long as the Formatter that created it. Object IDs have meaning only within a given serialized stream, and are used for tracking which objects have references to others within the serialized object graph.

Using a hash table, the ObjectIDGenerator retains which ID is assigned to which object. The object references, which uniquely identify each object, are addresses in the runtime garbage-collected heap. Object reference values can change during serialization, but the table is updated automatically so the information is correct.

Object IDs are 64-bit numbers. Allocation starts from one, so zero is never a valid object ID. A formatter can choose a zero value to represent an object reference whose value is a null reference (Nothing in Visual Basic).

Simon Svensson
Reflector tells me that ObjectIDGenerator is a hashtable relying on the default GetHashCode implementation (i.e. it does not use user overloads).
Anton Tykhyy
Probably the best solution when printable unique IDs are required.
romkyns
+1  A: 

You can develop your own thing in a second. For instance:

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

You can choose what you will like to have as unique ID on your own, for instance, System.Guid.NewGuid() or simply integer for fastest access.

majkinetor
Won't help if what you need this for is `Dispose` bugs, because this would prevent any kind of disposal.
romkyns
A: 

I know that this has been answered, but it's at least useful to note that you can use:

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

Which will not give you a "unique id" directly, but combined with WeakReferences (and a hashset?) could give you a pretty easy way of tracking various instances.

Andrew Theken