0

目前,我有这个代码:

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

4

2 回答 2

1

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

  • 使对象可保存+可恢复=可序列化
  • 与某种经理一起做(也许这很符合您对指针的想法),您可以通过闭包!

第二个选项解释:

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)和 viola 创建管理器 - 无需指针即可获得所需的语义!

于 2013-09-13T11:05:23.980 回答
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);
    }
  }
}
于 2013-09-13T11:03:48.003 回答