2010-05-13 22 views
5

Ho un progetto XNA 3.0 compilato correttamente in VS2008, ma che fornisce errori di compilazione in VS2010 (con XNA 4.0 CTP). L'errore:"Impossibile utilizzare locale fisso nell'espressione lambda"

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

depthPtr è un fixed float* in un array, che viene utilizzato all'interno di un'espressione Parallel.For lambda da System.Threading. Come ho detto, questo è stato compilato e funzionava bene su VS2008, ma non su VS2010, anche quando si utilizza .NET 3.5.

Questo è cambiato in .NET 4.0, e anche così, non dovrebbe ancora essere compilato quando scelgo .NET 3.5 come framework di destinazione? La ricerca del termine "Can not use fixed local" restituisce esattamente un risultato (inutile), sia in Google che in Bing.

Se questo è cambiato, qual è la ragione di questo? Posso immaginare che catturare un puntatore tipo fixed in una chiusura potrebbe essere un po 'strano, è per questo? Quindi immagino che questa sia una cattiva pratica? E prima che qualcuno chieda: no, l'uso dei puntatori non è assolutamente critico qui. Vorrei ancora sapere però :)

EDIT: Come richiesto, un esempio di codice (non dal mio programma, ovviamente) che riproduce l'errore:

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

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

Le compilazioni di cui sopra in VS2008 (beh, a parte il riferimento a Parallel, ma qualsiasi altra espressione lambda lo farà), ma non in VS2010.

+1

puoi pubblicare il codice che causa l'errore. – luke

+0

Beh, non c'è molto da pubblicare, è esattamente quello che ha detto l'errore: l'uso di un puntatore all'interno di un'espressione di lamdba. – JulianR

+0

Ancora, pubblicare un breve ma completo codice che possiamo sperimentare sarebbe una buona cosa (tm). –

risposta

3

pin fissi un puntatore per la durata del blocco. Se dovessi memorizzare il delegato da richiamare in seguito dopo che il blocco è stato chiuso, il garbage collector potrebbe spostare l'oggetto tra quando viene creato il lambda e quando viene richiamato il lambda. Per quanto riguarda il motivo per cui il targeting di un diverso framework non aiuta, questo perché viene applicato dal linguaggio/compilatore, non dal runtime (se fosse il runtime, sarebbe segnalato tramite un'eccezione o simile in fase di runtime, non dal compilatore in fase di compilazione).

+0

Ah, quindi imposta solo la versione runtime e non usa un compilatore precedente? Ha senso. Per quanto riguarda il blocco, sì, questo è un rischio, ma potrei altrettanto facilmente dichiarare un campo istanza sul tipo che vive anche più a lungo del blocco fisso, facendo in modo che punti a una posizione non valida. Eppure è ancora possibile. – JulianR

+0

Non penso che sarebbe possibile controllare staticamente tutti gli scenari in cui un puntatore aggiunto potrebbe non essere più valido, in definitiva qualcuno può sempre utilizzare GCHandle. Questo controllo probabilmente rappresenta semplicemente uno "sforzo migliore" per evitare i problemi non ovvi che possono derivare da un codice come questo. –

0

Una spiegazione potrebbe essere che il valore di una variabile viene preso all'esecuzione della chiusura, non alla definizione. Nel tuo esempio probabilmente non farebbe del male, ma in altri casi potrebbe essere. Quindi, per insegnare buone pratiche, è vietato del tutto di prevenire tutti i tipi di bug interessanti.

1

Il doco afferma che non è consentito accedere a codice non sicuro in un metodo anonimo e le stesse restrizioni si applicano a lambda, quindi penso che potrebbe essere il tuo problema. Hai il vero errore del compilatore no?

1

Questo funziona. Fondamentalmente abbiamo eliminato il lambda contenente un puntatore non sicuro e lo abbiamo sostituito con un delegato in un'istanza di una classe dichiarata all'interno del blocco 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; 
     } 
    } 

mi sembra .NET 4 aggiunto qualche tentativo a metà mandato a quel paese a non consentire l'accesso di memoria fissa nel compilatore. Nel blocco di codice precedente è possibile definire esterno al blocco fixed e accedere all'array dopo il blocco fixed. Quindi non è perfetto ...

1

Il compilatore è corretto per rifiutare quel codice. Fisso può essere usato solo su variabili locali, e le variabili catturate da una chiusura non sono variabili locali, vengono issate nella classe utilizzata per mantenere lo stato per la chiusura.

Problemi correlati