7

我有一个 XNA 3.0 项目,它在 VS2008 中编译得很好,但是在 VS2010 中产生了编译错误(使用 XNA 4.0 CTP)。错误:

不能在匿名方法、lambda 表达式或查询表达式中使用固定的本地“depthPtr”

depthPtr是一个fixed float*到一个数组中,它在一个Parallel.Forlambda 表达式中使用System.Threading。正如我所说,这在 VS2008 上编译和运行得很好,但在 VS2010 上却没有,即使针对 .NET 3.5 也是如此。

这在 .NET 4.0 中是否发生了变化,即便如此,当我选择 .NET 3.5 作为目标框架时,它不应该仍然编译吗?在 Google 和 Bing 中搜索“不能使用固定本地”一词只会产生一个(无用的)结果。

如果这种情况发生了变化,这是什么原因?我可以想象fixed在闭包中捕获指针类型会有点奇怪,这是为什么呢?所以我猜这是不好的做法?在有人问之前:不,指针的使用在这里并不是绝对关键的。不过我还是想知道:)

编辑: 根据要求,重现错误的代码示例(显然不是来自我的程序):

static unsafe void Main(string[] args)
{
  float[] array = new float[10];

  fixed (float* ptr = array)
  {
    Parallel.For(0, 10, i =>
    {
      ptr[i] = i;
    });
  }
}

以上在 VS2008 中编译(嗯,除了对 的引用Parallel,但任何其他 lambda 表达式都可以),但在 VS2010 中没有。

4

5 回答 5

3

固定在块的持续时间内固定一个指针。如果您要在块退出后存储委托以供稍后调用,则垃圾收集器可以在创建 lambda 和调用 lambda 之间移动对象。至于为什么针对不同的框架没有帮助,这是因为这是由语言/编译器而不是运行时强制执行的(如果是运行时,它将在运行时通过异常或类似的方式报告,而不是由编译时编译器)。

于 2010-05-13T01:32:41.543 回答
2

这行得通。基本上,我们消除了包含不安全指针的 lambda,并将其替换为fixed块内声明的类实例的委托。

    static unsafe void UnsafeTest(string[] args) {
        float[] array = new float[10];

        fixed(float* ptr = array) {
            UnsafeOps ops = new UnsafeOps();
            ops.p = ptr;

            Parallel.For(0, 10, ops.Lambda);
        }
    }

    unsafe class UnsafeOps {
        public float* p;
        public unsafe void Lambda(int value) {
            p[value] = value;
        }
    }

在我看来,.NET 4 在禁止编译器中的固定内存访问方面添加了一些半途而废的尝试。在上面的代码块中,您可以在块UnsafeOps之外定义并在fixed块之后访问数组fixed。所以它并不完美...

于 2010-05-13T01:57:53.563 回答
1

doco说您不允许以匿名方法访问不安全的代码,并且同样的限制适用于 lambda,所以我认为这可能是您的问题。你有没有实际的编译器错误?

于 2010-05-13T01:54:07.737 回答
1

编译器拒绝该代码是正确的。Fixed 只能用于局部变量,闭包捕获的变量不是局部变量,它们被提升到用于维护闭包状态的类中。

于 2010-05-13T02:05:11.187 回答
0

一种解释可能是,变量的值是在执行闭包时获取的,而不是定义。在您的示例中,它可能不会造成任何伤害,但在其他情况下可能会。所以为了传授良好的实践,完全禁止以防止各种有趣的错误。

于 2010-05-13T01:27:19.163 回答