我正在重构我的库,以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
仅在偶然的情况下才在旧框架中起作用,我是否也应该对它们应用此修复程序?
备注:
[MethodImpl(MethodImplOptions.NoInlining)]
,ReinterpretOld
但没有效果。哦,这是一个有趣的发现;这里发生的事情是您的本地人正在被优化-没有剩余的本地人,这意味着没有本地人.locals init
,这意味着其stackalloc
行为有所不同,并且不会擦除空间;
private static unsafe uint Reinterpret1()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1;
return *(uint*)bytes;
}
private static unsafe uint Reinterpret2()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1;
uint* asUint = (uint*)bytes;
return *asUint;
}
变成:
.method private hidebysig static uint32 Reinterpret1() cil managed
{
.maxstack 8
L_0000: ldc.i4.4
L_0001: conv.u
L_0002: localloc
L_0004: dup
L_0005: ldc.i4.1
L_0006: stind.i1
L_0007: ldind.u4
L_0008: ret
}
.method private hidebysig static uint32 Reinterpret2() cil managed
{
.maxstack 3
.locals init (
[0] uint32* numPtr)
L_0000: ldc.i4.4
L_0001: conv.u
L_0002: localloc
L_0004: dup
L_0005: ldc.i4.1
L_0006: stind.i1
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldind.u4
L_000a: ret
}
我想我会很高兴地说,这是一个编译器错误,或至少:一个不良的副作用和行为考虑到先前的决定已经到位,说“发出.locals初始化”,特别是尝试和保持stackalloc
理智-但是编译器人员是否同意取决于他们。
解决方法是:将stackalloc
空间视为未定义的空间(公平地说,这就是您要执行的操作);如果您希望它为零:请手动将其归零。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句