固定指向托管变量的指针

时间:2013-09-13 10:53:44

标签: c# .net pointers garbage-collection unsafe

目前,我有这段代码:

class SO
{
    public SO()
    {
        var ptrManager = new PointerManager();
        int i = 1;

        ptrManager.SavePointer(ref i);

        Console.WriteLine(i); // prints 1

        i = 2; //change value to 2 within the current scope
        Console.WriteLine(i); // prints 2

        ptrManager.SetValue(3); // change value to 3 in another (unsafe) scope
        Console.WriteLine(i); // prints 3
    }
}

unsafe class PointerManager
{
    private int* ptr;
    public void SavePointer(ref int i)
    {
        fixed (int* p = &i)
        {
            ptr = p;
        }
    }

    public void SetValue(int i)
    {
        *ptr = i;
    }
}

首先,我告诉PointerManager创建一个指向我的int i的指针。 这样,稍后,我就可以告诉PointerManager更改i的值,而无需再次传递i

到目前为止,这似乎运作良好。但是,我已经读过,如果GC决定在内存中移动i,那么我的指针ptr将变为无效,随后对SetValue的调用将具有不可预测的行为。

那么,如何阻止GC移动i,或者,是否有更好的方法来实现我的目标?

我也被告知只需添加GCHandle.Alloc(i, GCHandleType.Pinned);即可,但这似乎不正确......

修改:更具体地说,我会在GCHandle.Alloc方法的开头调用SavePointer

2 个答案:

答案 0 :(得分:1)

您可以使用Function和Action类型的强大功能来实现相同功能,而不会出现任何不安全的代码。尽量避免任何不安全的ocde或指针,如果你没有与非托管代码连接,你不需要它们:

using System;

namespace ConsoleApplication16
{
  class SO
  {
    public static void Main()
    {
      var manager = new LateSetter<int>();

      int i = 1;

      manager.SaveSetter(newValue => i = newValue);

      Console.WriteLine(i); // prints 1

      i = 2;
      Console.WriteLine(i); // prints 2

      manager.SetValue(3); // change value to 3
      Console.WriteLine(i); // prints 3

      Console.ReadLine();
    }
  }

  class LateSetter<T>
  {
    private Action<T> set;

    public void SaveSetter(Action<T> setter)
    {
      this.set = setter;
    }

    public void SetValue(T i)
    {
      this.set(i);
    }
  }
}

答案 1 :(得分:1)

通常你不想阻止GC移动任何东西 - 特别是在你的情况下(事务对象),我宁愿考虑这样的选项:

  • 使对象可以保存+ restorable = serializable
  • 与某种经理一起做(可能这与你用指针的想法非常吻合),你可以通过闭包!

第二个选项解释:

class VariableManager
{
    private Action<int> setter;
    public void VariableManager(Action<int> setter)
    {
        this.setter = setter;
    }

    public void SetValue(int i)
    {
        setter(i);
    }
}

只需通过new VariableManager(i => yourInt = i)和中提琴创建经理 - 您就可以在没有指针的情况下获得所需的语义!