Ich überarbeite meine Bibliotheken, Span<T>
um Heap-Zuweisungen nach Möglichkeit zu vermeiden, aber da ich auch ältere Frameworks anvisiere, implementiere ich auch einige allgemeine Fallback-Lösungen. Aber jetzt habe ich ein seltsames Problem gefunden und bin mir nicht ganz sicher, ob ich einen Fehler in .NET Core 3 gefunden habe oder ob ich etwas Illegales mache.
Die Angelegenheit:
// 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;
}
Interessanterweise ReinterpretOld
funktioniert es gut in .NET Framework und in .NET Core 2.0 (also könnte ich schließlich damit zufrieden sein), aber es stört mich ein bisschen.
Übrigens. ReinterpretOld
kann auch in .NET Core 3.0 durch eine kleine Änderung behoben werden:
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
Meine Frage:
Ist dies ein Fehler oder funktioniert es ReinterpretOld
in älteren Frameworks nur aus Versehen und sollte ich das Update auch für sie anwenden?
Bemerkungen:
- Der Debug-Build funktioniert auch in .NET Core 3.0
- Ich habe versucht, mich zu bewerben
[MethodImpl(MethodImplOptions.NoInlining)]
,ReinterpretOld
aber es hatte keine Wirkung.
quelle
return Unsafe.As<byte, uint>(ref bytes[0]);
oderreturn MemoryMarshal.Cast<byte, uint>(bytes)[0];
- keine Notwendigkeit zu verwendenGetPinnableReference()
; Blick in das andere StückSpan<T>
, werden zu unterschiedlichen IL kompiliert. Ich glaube nicht, dass Sie etwas Ungültiges tun: Ich vermute einen JIT-Fehler.stackalloc
(dh er löscht nicht den zugewiesenen Speicherplatz)Antworten:
Oh, das ist ein lustiger Fund. Was hier passiert, ist, dass Ihr Einheimischer optimiert wird - es gibt keine Einheimischen mehr, was bedeutet, dass es keine gibt
.locals init
, was bedeutet, dassstackalloc
sich das anders verhält und den Raum nicht abwischt;wird:
Ich denke, ich würde gerne sagen, dass dies ein Compiler-Fehler ist, oder zumindest: ein unerwünschter Nebeneffekt und Verhalten, da frühere Entscheidungen getroffen wurden, um "emit the .locals init" zu sagen , speziell um zu versuchen und Bleiben Sie
stackalloc
gesund - aber ob die Compiler-Leute zustimmen, liegt bei ihnen.Die Problemumgehung lautet: Behandeln Sie den
stackalloc
Raum als undefiniert (was, um fair zu sein, das ist, was Sie tun sollen); Wenn Sie erwarten, dass es Nullen sind: Stellen Sie es manuell auf Null.quelle
locals init
. Schön..maxstack
und.locals
machen es besonders einfach, nicht zu bemerken, dass es da ist / nicht da ist :)The content of the newly allocated memory is undefined.
laut MSDN. Die Spezifikation sagt nicht, dass der Speicher auch auf Null gesetzt werden sollte. Es sieht also so aus, als ob es nur versehentlich oder aufgrund eines nicht vertraglichen Verhaltens auf alten Frameworks funktioniert.