Dirección de memoria de un objeto en C #

Tengo una función escrita hace un tiempo (para .NET 3.5), y ahora que me actualicé a 4.0 no puedo hacer que funcione.

La función es:

public static class MemoryAddress { public static string Get(object a) { GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned); IntPtr pointer = GCHandle.ToIntPtr(handle); handle.Free(); return "0x" + pointer.ToString("X"); } } 

Ahora, cuando lo llamo – MemoryAddress.Get (auto nuevo (“azul”))

 public class Car { public string Color; public Car(string color) { Color = color; } } 

Me sale el error: “El objeto contiene datos no primitivos o no blittable”.

¿Por qué ya no funciona? ¿Cómo puedo obtener ahora la dirección de memoria de los objetos administrados?

Puede usar GCHandleType.Weak en lugar de Pineado. Por otro lado, hay otra forma de obtener puntero a un objeto:

 object o = new object(); TypedReference tr = __makeref(o); IntPtr ptr = **(IntPtr**)(&tr); 

Requiere un locking inseguro y es muy, muy peligroso y no debe usarse en absoluto. ☺

En lugar de este código, debe llamar a GetHashCode() , que devolverá un valor único (con suerte) para cada instancia.

También puede usar la clase ObjectIDGenerator , que se garantiza que es única.

Hay una solución mejor si realmente no necesita la dirección de la memoria, sino más bien algunos medios para identificar de manera única un objeto administrado:

 using System.Runtime.CompilerServices; public static class Extensions { private static readonly ConditionalWeakTable _ids = new ConditionalWeakTable(); public static Guid GetRefId(this T obj) where T: class { if (obj == null) return default(Guid); return _ids.GetOrCreateValue(obj).Id; } private class RefId { public Guid Id { get; } = Guid.NewGuid(); } } 

Esto es seguro para hilos y utiliza referencias débiles internamente, por lo que no tendrá pérdidas de memoria.

Puede usar cualquier medio de generación de claves que desee. Estoy usando Guid.NewGuid() aquí porque es simple y seguro para subprocesos.

Actualizar

Seguí adelante y creé un paquete Nuget Overby.Extensions.Attachments que contiene algunos métodos de extensión para unir objetos a otros objetos. Hay una extensión llamada GetReferenceId() que efectivamente hace lo que muestra el código en esta respuesta.

Cuando liberas ese identificador, el recolector de basura puede mover libremente la memoria que estaba anclada. Si tiene un puntero a la memoria que se supone que está inmovilizado, y usted anula esa memoria, entonces todas las apuestas están desactivadas. Que esto funcionó en 3.5 fue probablemente solo por suerte. El comstackdor JIT y el tiempo de ejecución para 4.0 probablemente hagan un mejor trabajo en el análisis de la duración del objeto.

Si realmente quieres hacer esto, puedes utilizar una try/finally para evitar que el objeto se desenganche hasta después de que lo hayas usado:

 public static string Get(object a) { GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned); try { IntPtr pointer = GCHandle.ToIntPtr(handle); return "0x" + pointer.ToString("X"); } finally { handle.Free(); } } 

Esto funciona para mí …

 #region AddressOf ///  /// Provides the current address of the given object. ///  ///  ///  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public static System.IntPtr AddressOf(object obj) { if (obj == null) return System.IntPtr.Zero; System.TypedReference reference = __makeref(obj); System.TypedReference* pRef = &reference; return (System.IntPtr)pRef; //(&pRef) } ///  /// Provides the current address of the given element ///  ///  ///  ///  [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public static System.IntPtr AddressOf(T t) //refember ReferenceTypes are references to the CLRHeader //where TOriginal : struct { System.TypedReference reference = __makeref(t); return *(System.IntPtr*)(&reference); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] static System.IntPtr AddressOfRef(ref T t) //refember ReferenceTypes are references to the CLRHeader //where TOriginal : struct { System.TypedReference reference = __makeref(t); System.TypedReference* pRef = &reference; return (System.IntPtr)pRef; //(&pRef) } ///  /// Returns the unmanaged address of the given array. ///  ///  ///  if null, otherwise the address of the array [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public static System.IntPtr AddressOfByteArray(byte[] array) { if (array == null) return System.IntPtr.Zero; fixed (byte* ptr = array) return (System.IntPtr)(ptr - 2 * sizeof(void*)); //Todo staticaly determine size of void? } #endregion 

Cambiar el tipo de alloc:

 GCHandle handle = GCHandle.Alloc(a, GCHandleType.Normal); 

Obtener la dirección de un objeto arbitrario en .NET no es posible, pero se puede hacer si cambias el código fuente y usas mono. Vea las instrucciones aquí: Obtenga la dirección de memoria del objeto .NET (C #)