2010-05-13 21 views
5

Tengo un proyecto XNA 3.0 que se compiló muy bien en VS2008, pero que da errores de compilación en VS2010 (con XNA 4.0 CTP). El error:"No se puede usar la expresión lambda interna local fija"

Cannot use fixed local 'depthPtr' inside an anonymous method, lambda expression, or query expression

depthPtr es un fixed float* en una matriz, que se utiliza dentro de una expresión Parallel.For lambda de System.Threading. Como dije, esto compiló y ejecutó muy bien en VS2008, pero no en VS2010, incluso cuando se dirige a .NET 3.5.

¿Esto ha cambiado en .NET 4.0, y aún así, no debería compilarse cuando elijo .NET 3.5 como el marco de destino? La búsqueda del término "No se puede usar local fijo" arroja exactamente un resultado (inútil), tanto en Google como en Bing.

Si esto ha cambiado, ¿cuál es el motivo? Me imagino que capturar un tipo de puntero fixed en un cierre podría ser un poco extraño, ¿es por eso? ¿Entonces supongo que esto es una mala práctica? Y antes de que nadie pregunte: no, el uso de punteros no es absolutamente crítico aquí. Todavía me gustaría saber, sin embargo :)

EDIT: a lo solicitado, un ejemplo de código (no de mi programa, obviamente) que reproduce el error:

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

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

Las compilaciones anteriores en VS2008 (bien, aparte de la referencia a Parallel, pero cualquier otra expresión lambda lo hará), pero no en VS2010.

+1

puede publicar el código que causa el error. – luke

+0

Bueno, no hay mucho para publicar, es exactamente lo que dijo el error: el uso de un puntero dentro de una expresión lamdba. – JulianR

+0

Aún así, publicar un fragmento de código breve pero completo con el que podamos experimentar sería una buena cosa (tm). –

Respuesta

3

pines fijos un puntero durante la duración del bloque. Si tuviera que almacenar el delegado para invocar más tarde después de que se hubiera salido del bloque, el recolector de elementos no utilizados podría mover el objeto entre el momento en que se crea el lambda y el momento en que se invoca el lambda. En cuanto a por qué la orientación a un marco diferente no ayuda, esto se debe a que el lenguaje/compilador lo aplica, no el tiempo de ejecución (si fuera el tiempo de ejecución, se informaría a través de una excepción o similar en tiempo de ejecución, no por el compilador en tiempo de compilación).

+0

Ah, ¿simplemente establece la versión de tiempo de ejecución y no usa un compilador anterior? Tiene sentido. Sobre la fijación, sí, eso es un riesgo, pero podría declarar fácilmente un campo de instancia en el tipo que también viva más tiempo que el bloque fijo, haciendo que apunte a una ubicación no válida. Sin embargo, eso aún es posible. – JulianR

+0

No creo que sea posible verificar estáticamente todos los escenarios que un puntero fijado podría quedar invalidado, en última instancia, alguien siempre puede usar GCHandle. Probablemente, este cheque represente el "mejor esfuerzo" para evitar los problemas no obvios que pueden surgir de un código como este. –

0

Una explicación podría ser, que el valor de una variable se toma en la ejecución del cierre, no la definición. En su ejemplo, probablemente no haría ningún daño, pero en otros casos podría ser. Entonces, para enseñar buenas prácticas, está prohibido evitar todo tipo de errores interesantes.

1

El doco dice que no se le permite acceder al código inseguro en un método anónimo, y las mismas restricciones se aplican a lambdas, por lo que creo que ese puede ser su problema. ¿Tiene el error real del compilador no?

1

Esto funciona. Básicamente eliminamos el lambda que contiene un puntero inseguro y lo reemplazamos con un delegado a una instancia de una clase declarada dentro del bloque 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; 
     } 
    } 

Me parece .NET 4 añade algún intento mitad arsed al no permitir el acceso de memoria fija en el compilador. En el bloque de código anterior, puede definir UnsafeOps fuera del bloque fixed y acceder a la matriz después del bloque fixed. Por lo tanto, no es perfecto ...

1

El compilador rechaza correctamente ese código. Fixed solo se puede usar en variables locales, y las variables capturadas por un cierre no son variables locales, sino que se izan en la clase utilizada para mantener el estado del cierre.

Cuestiones relacionadas