C#指针和值类型变量的重定位

C# pointer and relocation of value type variables

我写了以下不安全的 class,它包装了一个指向 int 的指针:

unsafe class PtrWrapper
{
   public PtrWrapper(ref int val)
   {
       fixed (int* ptr = &val)
       {
           m_ptr = ptr;
       }
   }
   public int Value
   {
       get { return *m_ptr; }
       set { *m_ptr = value; }
   }
   int * m_ptr;
}

我已经测试过了,它似乎工作正常,但是我刚刚再次阅读了 reference on fixed ,看起来所有对指针的操作都应该在语句中完成:

Without fixed, pointers to movable managed variables would be of little use since garbage collection could relocate the variables unpredictably.

那么,是否有可能在我调用值 属性 时指针对象已在内存中重新定位,而我的指针指向其他对象?我知道如果指针超出范围我会遇到问题,但我会通过我如何使用我的 class 来解释它。所以我问的是 relocating 值类型变量 not 超出范围。

是的,很有可能,甚至很有可能。

在堆上:

class M 
{
    public int i;
    public PtrWrapper w;

    public M()
    {
        i = 42;
        w = new PtrWrapper(ref i);
    }
}

var m = new M();
var value = m.w.Value; // probably 42

// move m to gen 2
GC.Collect();
GC.Collect();

value = m.w.Value; // probably a random value

这就是它被称为 不安全 的原因 :) fixed 只会阻止范围内的东西移动。

不过在堆栈上应该没问题。只有当你超出范围时,堆栈上的变量才会被弹出,例如在这种情况下:

PtrWrapper M()
{
    var i = 42;
    var w = new PtrWrapper(ref i);
    return w;
}

var w = M();
var value = w.Value; // some random value

请注意,捕获的变量(在 lambda 中)确实在堆上,因此在这种情况下您应该小心处理这些变量。