Refaktoryzuję swoje biblioteki, aby Span<T>
w miarę możliwości unikały alokacji sterty, ale ponieważ celuję również w starsze platformy, wdrażam również ogólne rozwiązania awaryjne. Ale teraz znalazłem dziwny problem i nie jestem do końca pewien, czy znalazłem błąd w .NET Core 3, czy robię coś nielegalnego.
Problem:
// 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;
}
Co ciekawe, ReinterpretOld
działa dobrze w .NET Framework i .NET Core 2.0 (więc w końcu mógłbym być z tego zadowolony), ale trochę mnie to niepokoi.
Btw. ReinterpretOld
można naprawić również w .NET Core 3.0 przez niewielką modyfikację:
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
Moje pytanie:
Czy to błąd, czy ReinterpretOld
działa w starszych frameworkach tylko przypadkowo i czy powinienem zastosować poprawkę również dla nich?
Uwagi:
- Kompilacja debugowania działa również w .NET Core 3.0
- Starałem się stosować
[MethodImpl(MethodImplOptions.NoInlining)]
doReinterpretOld
ale to nie miało żadnego wpływu.
stackalloc
(tzn. nie usuwa przydzielonego miejsca)
return Unsafe.As<byte, uint>(ref bytes[0]);
lubreturn MemoryMarshal.Cast<byte, uint>(bytes)[0];
- nie ma potrzeby korzystaniaGetPinnableReference()
; spoglądając w drugą stronę