Indirizzo di memoria di un object in C #

Ho una funzione scritta qualche tempo fa (per .NET 3.5), e ora che ho aggiornato a 4.0 non riesco a farlo funzionare.

La funzione è:

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"); } } 

Ora, quando lo chiamo – MemoryAddress.Get (new Car (“blue”))

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

Viene visualizzato l’errore: “L’object contiene dati non primitivi o non intercettabili”.

Perché non funziona più? Come posso ottenere ora l’indirizzo di memoria degli oggetti gestiti?

Puoi utilizzare GCHandleType.Weak anziché Pinned. D’altra parte, c’è un altro modo per ottenere il puntatore a un object:

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

Richiede il blocco non sicuro ed è molto, molto pericoloso e non dovrebbe essere usato affatto. ☺

Invece di questo codice, dovresti chiamare GetHashCode() , che restituirà un valore univoco (si spera) per ogni istanza.

Puoi anche usare la class ObjectIDGenerator , che è garantita come unica.

C’è una soluzione migliore se non si ha realmente bisogno dell’indirizzo di memoria, ma piuttosto di mezzi per identificare univocamente un object gestito:

 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(); } } 

Questo è thread-safe e utilizza internamente riferimenti deboli, quindi non avrai perdite di memoria.

Puoi usare qualsiasi mezzo di generazione delle chiavi che ti piace. Sto usando Guid.NewGuid() qui perché è semplice e sicuro.

Aggiornare

Sono andato avanti e ho creato un pacchetto Nuget Overby.Extensions.Attachments che contiene alcuni metodi di estensione per il collegamento di oggetti ad altri oggetti. C’è un’estensione chiamata GetReferenceId() che fa effettivamente ciò che mostra il codice in questa risposta.

Quando si libera quell’handle, il garbage collector è libero di spostare la memoria che è stata bloccata. Se si ha un puntatore alla memoria che si suppone debba essere bloccato, e si annulla la memoria, tutte le scommesse sono distriggerste. Che questo abbia funzionato del tutto nel 3.5 è stato probabilmente solo per fortuna. Il compilatore JIT e il runtime per 4.0 probabilmente svolgono un lavoro migliore nell’analisi della durata dell’object.

Se vuoi davvero farlo, puoi usare un try/finally per evitare che l’object rimanga sbloccato fino a dopo averlo usato:

 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(); } } 

Questo funziona per me …

 #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 

Cambia il tipo di allocazione:

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

Ottenere l’indirizzo di un object arbitrario in .NET non è ansible, ma può essere fatto se si modifica il codice sorgente e si usa mono. Vedi le istruzioni qui: Ottieni l’indirizzo di memoria di .NET Object (C #)