qué punteros programacion poo orientado orientada operaciones objetos objeto método memoria los lista herencia evento ejercicios ejemplos dinamica crean con como clases c# .net

punteros - Dirección de memoria de un objeto en C#



¿ en c# qué es método? (7)

Cambiar el tipo de alloc:

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

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?


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


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.


Esto funciona para mí ...

#region AddressOf /// <summary> /// Provides the current address of the given object. /// </summary> /// <param name="obj"></param> /// <returns></returns> [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) } /// <summary> /// Provides the current address of the given element /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public static System.IntPtr AddressOf<T>(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<T>(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) } /// <summary> /// Returns the unmanaged address of the given array. /// </summary> /// <param name="array"></param> /// <returns><see cref="IntPtr.Zero"/> if null, otherwise the address of the array</returns> [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


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<object, RefId> _ids = new ConditionalWeakTable<object, RefId>(); public static Guid GetRefId<T>(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 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.



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 bloqueo inseguro y es muy, muy peligroso y no debe usarse en absoluto. ☺