我正在重构我的库,以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中都可以很好地工作(因此我毕竟可以满意),但是,这仍然让我有些困扰。
顺便说一句。ReinterpretOld
只需稍作修改,即可在.NET Core 3.0中进行修复:
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
我的问题:
这是一个错误,还是ReinterpretOld
仅在偶然的情况下才在旧框架中起作用,我是否也应对此应用修复程序?
备注:
- 调试版本也可以在.NET Core 3.0中使用
- 我尝试申请
[MethodImpl(MethodImplOptions.NoInlining)]
,ReinterpretOld
但没有效果。
SharpLab可以帮助其他人。避免使用的两个版本可以
—
canton7 '19
Span<T>
编译为不同的IL。我认为您没有做任何无效的事情:我怀疑是JIT错误。
您看到的垃圾是什么?您是否在使用hack禁用locals-init?此hack会产生重大影响
—
Marc Gravell
stackalloc
(即不会擦除分配的空间)
@ canton7如果它们编译为相同的IL,我们不能推断出它是JIT错误...如果IL相同,等等...听起来更像是编译器错误,如果有的话,也许是使用较旧的编译器?György:您能确切说明如何编译吗?例如什么SDK?我无法复制垃圾
—
Marc Gravell
看起来stackalloc并不总是为零,实际上:link
—
canton7 '19
return Unsafe.As<byte, uint>(ref bytes[0]);
或return MemoryMarshal.Cast<byte, uint>(bytes)[0];
-无需使用GetPinnableReference()
;不过,他正在寻找另一部分