2010-05-13 4 views
5

Ich habe ein XNA 3.0-Projekt, das in VS2008 gut kompiliert, aber das kompiliert Fehler in VS2010 (mit XNA 4.0 CTP). Der Fehler:"Kann nicht festen lokalen innerhalb Lambda-Ausdruck verwenden"

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

depthPtr ist ein fixed float* in ein Array, das aus System.Threading innerhalb eines Parallel.For Lambda-Ausdruck verwendet wird. Wie gesagt, dies kompiliert und lief gut auf VS2008, aber nicht auf VS2010, auch wenn es .NET 3.5 anvisiert.

Hat sich das in .NET 4.0 geändert und sollte es trotzdem noch kompilieren, wenn ich .NET 3.5 als Zielframework wähle? Die Suche nach dem Begriff "Kann nicht lokal fixieren" ergibt genau ein (nutzloses) Ergebnis, sowohl bei Google als auch bei Bing.

Wenn sich das geändert hat, was ist der Grund dafür? Ich kann mir vorstellen, einen fixed Zeiger-Typ in einem Verschluss zu erfassen, könnte ein bisschen seltsam werden, ist das warum? Also ich vermute, das ist eine schlechte Übung? Und bevor jemand fragt: Nein, der Einsatz von Zeigern ist hier nicht absolut kritisch. Ich möchte noch wissen, aber :)

EDIT: Wie gewünscht, ein Codebeispiel (nicht von meinem Programm, natürlich), der den Fehler reproduziert:

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

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

Die oben compiliert in VS2008 (gut, abgesehen von der Referenz auf Parallel, aber jeder andere Lambda-Ausdruck wird tun, aber nicht in VS2010.

+1

können Sie bitte den Code posten, der den Fehler verursacht. – luke

+0

Nun, es gibt nicht viel zu posten, es ist genau das, was der Fehler sagte: die Verwendung eines Zeigers innerhalb eines Lamdba-Ausdrucks. – JulianR

+0

Noch ein kurzes, aber vollständiges Stück Code, mit dem wir experimentieren können, wäre ein Good Thing (tm). –

Antwort

3

fixiert Pins einen Zeiger für die Dauer des Blocks. Wenn Sie den Delegat speichern, der später aufgerufen werden soll, nachdem der Block beendet wurde, kann der Garbage Collector das Objekt zwischen dem Zeitpunkt der Lambda-Erstellung und dem Aufruf des Lambda verschieben. Warum das Targeting eines anderen Frameworks nicht hilft, liegt daran, dass dies von der Sprache/dem Compiler erzwungen wird, nicht von der Runtime (wenn es die Runtime wäre, würde sie zur Laufzeit von einer Exception o.ä. gemeldet und nicht von der Compiler zur Kompilierzeit).

+0

Ah, also setzt es nur die Laufzeitversion und verwendet keinen älteren Compiler? Macht Sinn. Über das Pinning, ja, das ist ein Risiko, aber ich könnte ebenso einfach ein Instanzfeld auf dem Typ deklarieren, das auch länger als der feste Block lebt und auf einen ungültigen Ort verweist. Aber das ist immer noch möglich. – JulianR

+0

Ich glaube nicht, dass es möglich wäre, alle Szenarien statisch zu überprüfen, in denen ein gepinnter Zeiger ungültig werden könnte, schließlich kann jemand immer GCHandle verwenden. Diese Überprüfung stellt wahrscheinlich einfach einen "besten Versuch" dar, um die nicht offensichtlichen Probleme zu vermeiden, die sich aus einem Code wie diesem ergeben können. –

0

Eine Erklärung könnte sein, dass der Wert einer Variablen bei der Ausführung des Abschlusses genommen wird, nicht die Definition. In Ihrem Beispiel würde es wahrscheinlich keinen Schaden anrichten, aber in anderen Fällen könnte es sein. Um also gute Praxis zu lehren, ist es verboten, alle Arten von interessanten Fehlern zu verhindern.

1

Die doco sagen, dass Sie nicht auf unsicheren Code in einer anonymen Methoden zugreifen dürfen, und die gleichen Einschränkungen gelten für Lambdas, also denke ich, dass das Ihr Problem sein kann. Hast du den tatsächlichen Compilerfehler nicht?

1

Das funktioniert. Im Grunde haben wir das Lambda, das einen unsicheren Zeiger enthält, eliminiert und es durch einen Delegaten einer Instanz einer Klasse ersetzt, die innerhalb des Blocks fixed deklariert wurde.

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; 
     } 
    } 

Sieht mir wie .NET 4 einige Halb arsed Versuch disallowing festen Speicherzugriff im Compiler hinzugefügt. Im obigen Codeblock könnten Sie UnsafeOps außerhalb des fixed Blocks definieren und auf das Array nach dem fixed-Block zugreifen. So ist es nicht perfekt ...

1

Der Compiler ist korrekt, diesen Code abzulehnen. Fixed kann nur für lokale Variablen verwendet werden, und Variablen, die von einer Schließung erfasst werden, sind keine lokalen Variablen. Sie werden in die Klasse geholt, die für die Aufrechterhaltung des Status für die Schließung verwendet wird.

Verwandte Themen