У меня есть проект XNA 3.0, который скомпилировал очень хорошо в VS2008, но это дает ошибки компиляции в VS2010 (с XNA 4.0 CTP). Ошибка:
Не может использовать зафиксированный локальный 'depthPtr' в анонимном методе, лямбда-выражении, или запросить выражение
depthPtr
a fixed float*
в массив, который используется внутри a Parallel.For
лямбда-выражение от System.Threading
. Как я сказал, это скомпилировало и работало очень хорошо на VS2008, но он не делает на VS2010, предназначаясь для.NET 3.5.
Это изменилось в.NET 4.0, и несмотря на это, она не должна все еще компилировать, когда я выбираю.NET 3.5 в качестве целевой платформы? Поиск термина "Не может использовать установленные локальные" урожаи точно один (бесполезный) результат, и в Google и в Bing.
Если это изменилось, какова причина этого? Я могу предположить получать a 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
, но любое другое лямбда-выражение сделает), но не делает в VS2010.
фиксирует указатель на время выполнения блока. Если бы вы хранили делегат для последующего вызова после выхода из блока, сборщик мусора мог бы переместить объект между моментом создания лямбды и моментом ее вызова. Что касается того, почему использование другого фреймворка не помогает, то это потому, что это навязывается языком/компилятором, а не временем выполнения (если бы это было время выполнения, то об этом сообщалось бы через исключение или что-то подобное во время выполнения, а не компилятором во время компиляции).
Компилятор правильно отклоняет этот код. Fixed может использоваться только для локальных переменных, а переменные, захваченные замыканием, не являются локальными переменными, они поднимаются в класс, используемый для поддержания состояния для закрытия.
Это работает. По сути, мы удалили лямбду, содержащую небезопасный указатель, и заменили ее делегатом экземпляра класса, объявленного внутри блока 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
. Так что это не идеально ...
Одно из объяснений может заключаться в том, что значение переменной берется при выполнении закрытия, а не при определении. В вашем примере это, вероятно, не повредит, но в других случаях может. Итак, чтобы научить хорошей практике, вообще запрещено предотвращать всевозможные интересные ошибки.
В doco говорится, что вам не разрешается обращаться к опасному коду в анонимных методах, и те же ограничения применяются к лямбдам, так что я думаю, что это может быть вашей проблемой. Нет ли у вас фактической ошибки компилятора?