C #에서 개체의 메모리 주소
얼마로 전에 함수 (.NET 3.5 용)가 있는데 이제 4.0 업그레이드했습니다.
작동 작동 수 없습니다.
기능은 다음과 가변합니다.
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");
}
}
이제 내가 그것을 호출하면 -MemoryAddress.Get (new Car ( "blue"))
public class Car
{
public string Color;
public Car(string color)
{
Color = color;
}
}
오류가 발생합니다.
객체에 원시 데이터가 아니거나 블리트 할 수없는 데이터가 있습니다.
왜 더 이상 작동하지 않습니까?
이제 관리 대상 개체의 메모리 주소를 어떻게 관리합니까?
고정 된 대신 GCHandleType.Weak을 사용할 수 있습니다. 반면에 다른 방법에 대한 포인터를 얻을 수 있습니다.
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
안전하지 않은 블록이 필요하며 매우 위험합니다. ☺
C #에서 by-ref 로컬을 사용할 수 없습니다 __makeref
.
object o = new object();
ref object r = ref o;
//roughly equivalent to
TypedReference tr = __makeref(o);
TypedReference 가 "generic" 이라는 점에는 한 가지 중요한 차이점이 있습니다 . 모든 유형의 변수에 대한 참조를 저장하는 데 사용할 수 있습니다. 일치하지 참조에 액세스하려는 해당 유형 (예 :)을 지정 __refvalue(tr, object)
해야합니다.
유형 검사를 구현 비용 TypedReference에 두 개의 필드가 있어야합니다. 하나에는 변수에 대한 실제 주소가 있고 다른 하나에는 유형 표현에 대한 포인터가 있습니다. 주소가 첫 번째 필드 인 경우에도 마찬가지입니다.
따라서 __makeref
는 변수에 대한 참조를 먼저 사용 o
합니다. 캐스트 (IntPtr**)(&tr)
는 구조를 IntPtr*
포인터를 통해 액세스되는 (일반 포인터 유형에 대한 포인터)의 배열 ( 포인터를 통해 표시됨)로 처리됩니다. 포인터는 첫 번째 필드를 다시 위해 먼저 역 참조되고, 그 다음에는 변수에 실제로 필요한 값 ( o
자체에 포인터 포인터)을 위해 참조 됩니다.
그러나 2012 년부터 더 안전한 솔루션을 찾았습니다.
public static class ReferenceHelpers
{
public static readonly Action<object, Action<IntPtr>> GetPinnedPtr;
static ReferenceHelpers()
{
var dyn = new DynamicMethod("GetPinnedPtr", typeof(void), new[] { typeof(object), typeof(Action<IntPtr>) }, typeof(ReferenceHelpers).Module);
var il = dyn.GetILGenerator();
il.DeclareLocal(typeof(object), true);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Conv_I);
il.Emit(OpCodes.Call, typeof(Action<IntPtr>).GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
GetPinnedPtr = (Action<object, Action<IntPtr>>)dyn.CreateDelegate(typeof(Action<object, Action<IntPtr>>));
}
}
이렇게하면 먼저 생성되는 개체를 고정한 다음 (관리되는 힙에서 저장소가 이동하지 않도록) 해당 주소를받는 대리 튼튼 실행하는 동적 메서드가됩니다. 대리 튼튼 실행하는 동안 개체는 여전히 고정되어 있으므로 포인터를 통해 안심하고 조작 할 수 있습니다.
object o = new object();
ReferenceHelpers.GetPinnedPtr(o, ptr => Console.WriteLine(Marshal.ReadIntPtr(ptr) == typeof(object).TypeHandle.Value)); //the first pointer in the managed object header in .NET points to its run-time type info
GCHandle 은 개체 를 고정하기 위해 유형을 blittable로 설정해야하는 방법 개체를 고정하는 가장 쉬운 방법 입니다. 구현 세부 사항, 문서화되지 않은 키워드 및 메모리 해킹을 사용하지 않는 이점이 있습니다.
이 코드 대신 호출해야합니다 GetHashCode()
. 각 인스턴스에 대해 고유 한 값을 반환합니다.
메모리 주소가 필요하지 않고 관리 대상 개체를 고유하게 포착하는 수단이 필요한 경우 더 나은 솔루션이 있습니다.
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();
}
}
이것은 사용되는 메모리 누수가 발생하는 안전하며 내부적으로 약한 참조를 사용합니다.
원하는 키 생성 수단을 사용할 수 있습니다. Guid.NewGuid()
간단하고 코드로부터 안전하기 때문에 여기서 사용 하고 있습니다.
최신 정보
계속해서 해서 다른 개체에 개체를 연결하기위한 일부 확장 메서드가 포함 된 Nuget 패키지 Overby.Extensions.Attachments 를 만들었습니다 . GetReferenceId()
이 문장의 확장 이 있습니다.
해당 핸들을 해제하면 가비지 수집기가 고정 된 메모리를 자유롭게 사용할 수 있습니다. 고정되어야하는 메모리에 대한 포인터가 있고 해당 메모리를 고정 해제하면 모든 것이 해제됩니다. 이것은 3.5에서 작동하지 않는 것이 아마도 운이 좋을 것입니다. JIT 컴파일러와 4.0 용 가동은 아마도 개체 수명 분석 작업을 더 잘 수행 할 것입니다.
이 작업을 수행하려는 경우 사용 try/finally
하여 개체가 고정 해제되지 않도록 할 수 있습니다 .
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();
}
}
이것은 나를 위해 작동합니다 ...
#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
할당 유형을 전환합니다.
GCHandle handle = GCHandle.Alloc(a, GCHandleType.Normal);
안전하지 않은 코드 나 객체 고정을 포함하지 않는 간단한 방법이 있습니다. 또한 역으로 작동합니다 (주소의 객체) :
public static class AddressHelper
{
private static object mutualObject;
private static ObjectReinterpreter reinterpreter;
static AddressHelper()
{
AddressHelper.mutualObject = new object();
AddressHelper.reinterpreter = new ObjectReinterpreter();
AddressHelper.reinterpreter.AsObject = new ObjectWrapper();
}
public static IntPtr GetAddress(object obj)
{
lock (AddressHelper.mutualObject)
{
AddressHelper.reinterpreter.AsObject.Object = obj;
IntPtr address = AddressHelper.reinterpreter.AsIntPtr.Value;
AddressHelper.reinterpreter.AsObject.Object = null;
return address;
}
}
public static T GetInstance<T>(IntPtr address)
{
lock (AddressHelper.mutualObject)
{
AddressHelper.reinterpreter.AsIntPtr.Value = address;
return (T)AddressHelper.reinterpreter.AsObject.Object;
}
}
// I bet you thought C# was type-safe.
[StructLayout(LayoutKind.Explicit)]
private struct ObjectReinterpreter
{
[FieldOffset(0)] public ObjectWrapper AsObject;
[FieldOffset(0)] public IntPtrWrapper AsIntPtr;
}
private class ObjectWrapper
{
public object Object;
}
private class IntPtrWrapper
{
public IntPtr Value;
}
}
.NET에서 임의의 객체의 주소를 얻는 것은 불가능하지만 소스 코드를 변경하고 mono를 사용하면 가능합니다. 여기 지침을 참조하십시오. .NET 개체의 메모리 주소 가져 오기 (C #)
참고 URL : https://stackoverflow.com/questions/4994277/memory-address-of-an-object-in-c-sharp
'ProgramingTip' 카테고리의 다른 글
varchar (MAX)가 항상 선호하는 검사? (0) | 2020.11.15 |
---|---|
읽어야하는 PEP는 무엇입니까? (0) | 2020.11.15 |
iOS 8에서 작동하지 않는 Segue 해제 (0) | 2020.11.15 |
reCAPTCHA CAPTCHA가 점점 어려워 지나요 아니면 나만 (0) | 2020.11.15 |
C ++ : 함수 / 메소드 선언에서 앰퍼샌드 "&"와 별표 "*"의 차이점은 무엇입니까? (0) | 2020.11.15 |