使用 Guid 的显式结构布局

Explicit struct layout with Guid

我正在尝试有一个明确的结构布局,我在其中将字节数组重新解释为 guid。这在 .net 4.7 和 .net 4.7.2 的 32 位 运行ning 时工作正常。但是当你 运行 它在 64 位时它在 .NET 4.7 中工作,但在 .NET 4.7.2 中似乎不起作用。此代码无效还是 .NET 中的错误?

[更新]:测试了更多配置。适用于 4.6.2 和 4.7,但不适用于 4.7.1 和 4.7.2。

[更新 2]:查看反汇编。在 64 位中,分配导致无操作。所以我认为很明显这是 .net 中的错误:

32位反汇编:

TestStruct t = new TestStruct();
03010480  lea         edi,[ebp-58h]
03010483  xorps       xmm0,xmm0
03010486  movq        mmword ptr [edi],xmm0
0301048A  movq        mmword ptr [edi+8],xmm0
0301048F  movq        mmword ptr [edi+10h],xmm0
03010494  movq        mmword ptr [edi+18h],xmm0

t.Guid1 = Guid.NewGuid();
03010499  lea         eax,[ebp-58h]
0301049C  mov         dword ptr [ebp-64h],eax
0301049F  lea         ecx,[ebp-74h]
030104A2  call        723628F0
030104A7  mov         edi,dword ptr [ebp-64h]
030104AA  lea         esi,[ebp-74h]
030104AD  movq        xmm0,mmword ptr [esi]
030104B1  movq        mmword ptr [edi],xmm0
030104B5  movq        xmm0,mmword ptr [esi+8]
030104BA  movq        mmword ptr [edi+8],xmm0

t.Guid2 = t.Guid1;
030104BF  lea         edi,[ebp-58h]
030104C2  add         edi,10h
030104C5  lea         esi,[ebp-58h]
030104C8  movq        xmm0,mmword ptr [esi]
030104CC  movq        mmword ptr [edi],xmm0
030104D0  movq        xmm0,mmword ptr [esi+8]
030104D5  movq        mmword ptr [edi+8],xmm0

64 位反汇编:

TestStruct t = new TestStruct();
00007FFB054604B2  lea         rcx,[rbp+78h]
00007FFB054604B6  vxorpd      xmm0,xmm0,xmm0
00007FFB054604BB  vmovdqu     xmmword ptr [rcx],xmm0
00007FFB054604C0  vmovdqu     xmmword ptr [rcx+10h],xmm0

t.Guid1 = Guid.NewGuid();
00007FFB054604C6  lea         rcx,[rbp+78h]
00007FFB054604CA  mov         qword ptr [rbp+68h],rcx
00007FFB054604CE  lea         rcx,[rbp+58h]
00007FFB054604D2  call        00007FFB639BE8C0
00007FFB054604D7  mov         rax,qword ptr [rbp+68h]
00007FFB054604DB  vmovdqu     xmm0,xmmword ptr [rbp+58h]
00007FFB054604E1  vmovdqu     xmmword ptr [rax],xmm0

t.Guid2 = t.Guid1;
00007FFB054604E6  nop

重现代码:

[StructLayout(LayoutKind.Explicit, Size = SIZE)]
internal unsafe struct TestStruct
{
    public const int SIZE = 32;

    [FieldOffset(0)]
    private fixed byte _data[SIZE];

    [FieldOffset(0), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
    public Guid Guid1;

    [FieldOffset(16), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
    public Guid Guid2;
}

internal class Program
{
    private static void Main()
    {
        TestStruct t = new TestStruct();
        t.Guid1 = Guid.NewGuid();
        t.Guid2 = t.Guid1;

        if (t.Guid1 != t.Guid2)
        {
            throw new InvalidOperationException("Guids aren't equal");
        }
    }
}

我认为问题可能与 FieldOffset 有关。 FieldOffset(16) 正在置换字节数组。 如果封装该字段并使用 属性 则应该没有问题。

[StructLayout(LayoutKind.Explicit, Size = SIZE)]
internal unsafe struct TestStruct
{
    public const int SIZE = 32;

    [FieldOffset(0)]
    public fixed byte _data[SIZE];

    [FieldOffset(0), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
    private Guid guid1;

    [FieldOffset(16), MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
    private Guid guid2;

    public Guid Guid1 { get => guid1; set => guid1 = value; }
    public Guid Guid2 { get => guid2; set => guid2 = value; }
}

事实证明这是 RyuJIT 中的一个错误并通过以下拉取请求解决。

https://github.com/dotnet/coreclr/pull/17971