__makeref作为在C#中获取参考值的一种方法?

时间:2019-04-04 11:21:52

标签: c# clr

我已经看过这段代码,用于显示参考值:

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

结果(符合预期):

enter image description here

(我知道这里有字符串实习生,但这不是问题)。

问题:

尽管看起来确实可以做到, 使用__makeref是否是在c#中获取参考值的正确方法?

或者在任何情况下,此^都会失败... ??

1 个答案:

答案 0 :(得分:3)

  

尽管看起来确实可以完成,但是使用__makeref是否是在c#中获取参考值的正确方法?

在C#中没有做到这一点的“正确方法”-这不是您要尝试做的事情,而是:就做什么而言-这实际上是依靠TypedReference的内部布局和类型强制;它会起作用(只要TypedReference在内部不发生变化-例如TypeValue字段的顺序会发生变化),但是。真讨厌。

有一种更直接的方法;在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和类型强制的未记录和不受支持的语言功能“更好”:这是一个争论的问题。希望纯学术辩论!