Я делаю рефакторинг своих библиотек, чтобы Span<T>
по возможности избегать выделения кучи, но поскольку я нацеливаюсь и на более старые фреймворки, я также реализую некоторые общие резервные решения. Но теперь я обнаружил странную проблему и не совсем уверен, нашел ли я ошибку в .NET Core 3 или я делаю что-то нелегальное.
Проблема:
// This returns 1 as expected but cannot be used in older frameworks:
private static uint ReinterpretNew()
{
Span<byte> bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return Unsafe.As<byte, uint>(ref bytes.GetPinnableReference());
}
// This returns garbage in .NET Core 3.0 with release build:
private static unsafe uint ReinterpretOld()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return *(uint*)bytes;
}
Интересно, что он ReinterpretOld
хорошо работает в .NET Framework и .NET Core 2.0 (так что я мог бы быть доволен этим в конце концов), тем не менее, это немного беспокоит меня.
Btw. ReinterpretOld
может быть исправлено также в .NET Core 3.0 с помощью небольшой модификации:
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
Мой вопрос:
Это ошибка или она ReinterpretOld
работает в старых фреймворках только случайно, и я должен применить исправление также для них?
Примечания:
- Отладочная сборка работает также в .NET Core 3.0
- Я пытался применить
[MethodImpl(MethodImplOptions.NoInlining)]
к ,ReinterpretOld
но это не имело никакого эффекта.
источник
return Unsafe.As<byte, uint>(ref bytes[0]);
илиreturn MemoryMarshal.Cast<byte, uint>(bytes)[0];
- нет необходимости использоватьGetPinnableReference()
; хотя заглядываем в другоеSpan<T>
, компилируются в разные IL. Я не думаю, что вы делаете что-то недействительное: я подозреваю, что ошибка JIT.stackalloc
(то есть неОтветы:
Ох, это забавная находка; то, что происходит здесь, это то, что ваш местный становится оптимизированным - там не осталось местных жителей, что означает, что их нет
.locals init
, что означает, чтоstackalloc
ведет себя по- разному и не стирает пространство;будет выглядеть так:
Я думаю, что я был бы рад сказать, что это ошибка компилятора, или, по крайней мере, нежелательный побочный эффект и поведение, учитывая, что были приняты предыдущие решения, чтобы сказать «испустить init .locals» , в частности, чтобы попытаться оставайтесь в
stackalloc
здравом уме - но согласны ли люди компилятора, решать им.Обходной путь: обрабатывать
stackalloc
пространство как неопределенное (что, честно говоря, это то, что вы должны делать); если вы ожидаете, что это будут нули: вручную обнулите его.источник
locals init
. Хороший..maxstack
и.locals
, особенно легко заметить, что он есть / не существует :)The content of the newly allocated memory is undefined.
в соответствии с MSDN. В спецификации не сказано, что память также должна быть обнулена. Таким образом, похоже, что он работает на старой платформе только случайно или в результате недоговорного поведения.