我已经看过这段代码,用于显示参考值:
static void Main(string[] args)
{
string s1 = "ab";
string s2 = "a"+"b";
string s3 = new StringBuilder("a").Append("b").ToString();
Console.WriteLine(GetMemoryAddress(s1));
Console.WriteLine(GetMemoryAddress(s2));
Console.WriteLine(GetMemoryAddress(s3));
}
static IntPtr GetMemoryAddress(object s1)
{
unsafe
{
TypedReference tr = __makeref(s1);
IntPtr ptr = **(IntPtr**) (&tr);
return ptr;
}
}
结果(符合预期):
(我知道这里有字符串实习生,但这不是问题)。
问题:
尽管看起来确实可以做到,
使用__makeref
是否是在c#中获取参考值的正确方法?
或者在任何情况下,此^都会失败... ??
答案 0 :(得分:3)
尽管看起来确实可以完成,但是使用
__makeref
是否是在c#中获取参考值的正确方法?
在C#中没有做到这一点的“正确方法”-这不是您要尝试做的事情,而是:就做什么而言-这实际上是依靠TypedReference
的内部布局和类型强制;它会起作用(只要TypedReference
在内部不发生变化-例如Type
和Value
字段的顺序会发生变化),但是。真讨厌。
有一种更直接的方法;在IL中,您可以静默地从托管指针转换为非托管指针。这意味着您可以做一些讨厌的事情:
unsafe delegate void* RefToPointer(object obj);
static RefToPointer GetRef { get; } = MakeGetRef();
static RefToPointer MakeGetRef()
{
var dm = new DynamicMethod("evil", typeof(void*), new[] { typeof(object) });
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
return (RefToPointer)dm.CreateDelegate(typeof(RefToPointer));
}
现在您可以这样做:
var ptr = new IntPtr(GetRef(o));
Console.WriteLine(ptr);
这很糟糕,您永远都不要这样做-当然,GC可以在您不看的时候(甚至在您看的时候)移动东西。 ,但是...有效。
ref-emit是否比__makeref
和类型强制的未记录和不受支持的语言功能“更好”:这是一个争论的问题。希望纯学术辩论!