"Невозможно использовать фиксированный локальный внутри лямбда-выражения" - PullRequest
5 голосов
/ 13 мая 2010

У меня есть проект XNA 3.0, который прекрасно компилируется в VS2008, но дает ошибки компиляции в VS2010 (с XNA 4.0 CTP). Ошибка:

Невозможно использовать фиксированный локальный параметр deepPtr внутри анонимного метода, лямбда-выражения или выражения запроса

depthPtr - это fixed float* в массиве, который используется внутри Parallel.For лямбда-выражения из System.Threading. Как я уже сказал, это скомпилировано и отлично работает на VS2008, но не на VS2010, даже при использовании .NET 3.5.

Изменилось ли это в .NET 4.0 и, даже если это так, разве он не должен компилироваться, когда я выбираю .NET 3.5 в качестве целевой платформы? Поиск по термину «Невозможно использовать фиксированный локальный» дает ровно один (бесполезный) результат, как в Google, так и в Bing.

Если это изменилось, в чем причина? Я могу себе представить, что захват fixed типа указателя в замыкании может стать немного странным, поэтому? Так что я предполагаю, что это плохая практика? И прежде чем кто-либо спросит: нет, использование указателей здесь не является абсолютно критичным. Я все еще хотел бы знать, хотя:)

EDIT: В соответствии с просьбой, образец кода (очевидно, не из моей программы), который воспроизводит ошибку:

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, но подойдет любое другое лямбда-выражение), но не в VS2010.

Ответы [ 5 ]

3 голосов
/ 13 мая 2010

фиксированный выводит указатель на длительность блока. Если вы хотите сохранить делегат для последующего вызова после выхода из блока, сборщик мусора может переместить объект между созданием лямбды и вызовом лямбды. Что касается того, почему нацеливание на другую инфраструктуру не помогает, это потому, что это обеспечивается языком / компилятором, а не средой выполнения (если бы это была среда выполнения, об этом сообщалось бы через исключение или подобное во время выполнения, а не компилятор во время компиляции).

1 голос
/ 13 мая 2010

Компилятор корректен, чтобы отклонить этот код. Фиксированное можно использовать только с локальными переменными, а переменные, захваченные замыканием, не являются локальными переменными, они включены в класс, используемый для поддержания состояния замыкания.

1 голос
/ 13 мая 2010

Это работает. В основном мы исключили лямбду, содержащую небезопасный указатель, и заменили ее делегатом экземпляру класса, объявленного внутри блока 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. Так что это не идеально ...

1 голос
/ 13 мая 2010

doco говорит, что вам запрещен доступ к небезопасному коду анонимными методами, и те же ограничения применяются к лямбдам, поэтому я думаю, что это может быть вашей проблемой. У вас нет фактической ошибки компилятора?

0 голосов
/ 13 мая 2010

Одним из объяснений может быть то, что значение переменной берется при выполнении замыкания, а не определения. В вашем примере это, вероятно, не принесет никакого вреда, но в других случаях это может быть. Поэтому, чтобы научить хорошей практике, вообще запрещено предотвращать всевозможные интересные ошибки.

...