4

I'm trying to debug some weird behavior of multithreaded code and I need some way to reliably track object identity. In C++ I'd just get object addresses and convert them into strings and that way I'd know if the two addresses were of the same object or not.

Looks like a reference is a full equivalent of a C++ object address in C#.

Can I convert a reference into a string address representation?

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • 3
    What's wrong with `ReferenceEquals`? If it's a comparison across time, of course this won't work since .NET objects can move due to GC. – Damien_The_Unbeliever Nov 06 '12 at 09:08
  • @Damien_The_Unbeliever: That will require some internal list of objects and internal comparison. I'd prefer to just look into logs and draw conclusions from there. – sharptooth Nov 06 '12 at 09:17
  • So, it is a comparison across time then? – Damien_The_Unbeliever Nov 06 '12 at 09:20
  • Use the [unsafe](http://msdn.microsoft.com/en-us/library/chfa2zb8(v=vs.71).aspx) keyword. – Omar Nov 06 '12 at 09:22
  • @Damien_The_Unbeliever: It'd be convenient for me to just have the addresses in the log that to write a lot of extra code for maintaining the list and finding which object is which. I need some direct equivalent of C++ `printf("%p")`. – sharptooth Nov 06 '12 at 09:28
  • @sharptooth actually you have pointed, that your goal is *to know if the two addresses were of the same object or not*. In this case you don't need addresses - true/false will be enough. – Sergey Berezovskiy Nov 06 '12 at 09:32
  • @lazyberezovsky: Yes, that would be the cleanest way, but that would require a lot of extra code. Just `printf("%p")` would be more convenient for my scenario. – sharptooth Nov 06 '12 at 11:18
  • There is *no* simple, numeric identifier associated with each instance of a reference object. If you need such identifiers, the best approach may be to add them yourself. As I (and others) indicated, references, whilst numeric, can change at any time. – Damien_The_Unbeliever Nov 06 '12 at 11:20
  • @Damien_The_Unbeliever: Okay, let them change at any time, I still want the address as with `printf("%p")`. – sharptooth Nov 06 '12 at 11:59

3 Answers3

3

You can convert a reference to a pointer, and then to a number, but that's not a reliable way to track an object in C#, as the garbage collector can change a reference at any time. Once you copy the value from the reference, you can't know that it still represents the reference.

However, you can safely compare references. As long as the reference is still a reference, the garbage collector makes sure that it's up to date. If the garbage collector moves an object, it updates all references to it.

So, you can safely use the reference as identity for an object, as long as you keep it a reference. You can compare two references to see if they point to the same object or not.

If you convert the value of a reference to a different form, you get a flash copy of what the reference was at that moment. While it may still be useful to see that value, it's not 100% reliable, i.e. just because the representation of two references are different doesn't mean that they can't point to the same object, because the references could have changed between copying the first one and the second one.

That said, here's how you can get a pointer to an object and convert it to an IntPtr:

string str = "asdfasdf";
IntPtr p;

unsafe {
  // tell GC not to move the object, so that we can use a pointer to it
  fixed (void* ptr = str) {
    // here the object stays in place
    // make an IntPtr from the pointer, so we can keep it outside the fixed block
    p = new IntPtr(ptr);
  }
  // now the object can move again
}

Console.WriteLine(p);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • Well, the compiler says it won't implicitly compile `object` to `void*`. – sharptooth Nov 06 '12 at 11:08
  • @sharptooth: Right... it works a little different with different data types... I actually tested it with a string, and that works that way. – Guffa Nov 06 '12 at 12:14
0

In a way, you can:

GCHandle.Alloc(obj, GCHandleType.Pinned).AddrOfPinnedObject().ToString();

Please do not use this as a way to debug your multi-threaded app. Do as Damien pointed.

avishayp
  • 706
  • 5
  • 9
0

Create a ConditionalWeakTable<Object,String>, and any time you see each object that isn't in the table give it some sort of name and store it in the table. You may then without difficulty print out a name for any object you've seen before simply by using the table. If an object ceases to exist, the table entry will evaporate, so there's no need to worry about the table filling up with information related to objects that have long since been abandoned.

supercat
  • 77,689
  • 9
  • 166
  • 211