【问题标题】:C# StructLayout.Explicit QuestionC# StructLayout.Explicit 问题
【发布时间】:2010-11-14 01:03:16
【问题描述】:

我试图理解为什么下面的第二个示例没有问题,但第一个示例给了我下面的异常。在我看来,这两个例子都应该根据描述给出一个例外。谁能赐教?

未处理的异常: System.TypeLoadException:不能 从加载类型“StructTest.OuterType” 程序集'StructTest,版本= 1.0.0.0, 文化=中立,PublicKeyToken=null' 因为它包含一个对象字段 未正确对齐的偏移量 0 或被非对象字段重叠。
在 StructTest.Program.Main(字符串 [] args) 按任意键继续。 . .

示例 1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace StructTest
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct InnerType
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        char[] buffer;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct OuterType
    {
        [FieldOffset(0)]
        int someValue;

        [FieldOffset(0)]
        InnerType someOtherValue;
    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterType t = new OuterType();
            System.Console.WriteLine(t);
        }
    }
}

示例 2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace StructTest
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct InnerType
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
        char[] buffer;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct OuterType
    {
        [FieldOffset(4)]
        private int someValue;

        [FieldOffset(0)]
        InnerType someOtherValue;

    }

    class Program
    {
        static void Main(string[] args)
        {
            OuterType t = new OuterType();
            System.Console.WriteLine(t);
        }
    }
}

【问题讨论】:

    标签: c# .net clr structlayout


    【解决方案1】:

    公共语言运行时包含一个验证程序,可确保正在运行的代码(可验证的 IL)不会损坏托管环境中的内存。这可以防止您声明这样一个字段重叠的结构。基本上,您的结构包含两个数据成员。一个整数(4 个字节)和一个本机整数(指针大小)。在您可能正在运行代码的 32 位 CLR 上,char[] 将占用 4 个字节,因此如果您将整数放在距离结构开头不到 4 个字节的位置,您将有重叠的字段。有趣的是,您的两个代码 sn-ps 在 64 位运行时均失败,因为指针大小为 8 个字节。

    【讨论】:

    • 我明白了,所以如果我想正确执行此操作,以便在 32 或 64 位机器上正常工作,我需要分别使用 8 和 0 的偏移量,对吗?问题是我真正想要创建的是一个联合,如果我不能使用相同的偏移量,它看起来最终不会是一个。
    • 是的。它适用于偏移量为 8 和 0 的 x64 CLR。​​请注意,虽然代码 sn-p 有效,但如果您在现实世界的应用程序中执行此操作,您可能会与一些非托管的东西交互,这些东西会期望精确的偏移量。如果你将它设置为 8,它可能会在 32 位机器上失败(不是这个 sn-p 本身,而是你正在与之交互的非托管代码)。
    • 顺便说一下,如果它们是 非托管类型,你可以有重叠的字段,但是 .NET 数组是一个引用类型(它不能根据 C# 规范被视为非托管类型)。因此,您不能将引用类型作为 C# 中的联合成员。如果你真的需要这个,你应该考虑使用指针类型之类的东西作为结构成员而不是数组。
    • 谢谢。我正在研究直接在 OuterType 中使用大小为 100 的固定字符的不安全结构,而不是使用 InnerType。听起来这可能是更好的方法。
    • 规则更加晦涩难懂。引用类型的两个字段(甚至完全不相关)可以重叠,在这种情况下,程序集是有效的,但不可验证(因此它将在 FullTrust 中运行)。所以你仍然可以通过重叠对不兼容类型的引用来搞砸事情。我认为“部分重叠”限制的原因是因为它绝对保证将GC吹出水面。而使用完全引用重叠 GC 就可以了,而且当您有一个鉴别器字段来跟踪类型时,它实际上会很有用(节省强制转换运行时类型检查)。
    【解决方案2】:

    我想我会用我用来创建工会的解决方案来回应——这是我的初衷。我使用了一个不安全的结构和一个固定数组,然后使用一个属性与固定数组进行交互。我相信这应该做我想做的。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace StructTest
    {
    
        [StructLayout(LayoutKind.Explicit)]
        unsafe struct OuterType
        {
            private const int BUFFER_SIZE = 100;
    
            [FieldOffset(0)]
            private int transactionType;
    
            [FieldOffset(0)]
            private fixed byte writeBuffer[BUFFER_SIZE];
    
            public int TransactionType
            {
                get { return transactionType; }
                set { transactionType = value; }
            }
    
            public char[] WriteBuffer
            {
                set
                {
                    char[] newBuffer = value;
    
                    fixed (byte* b = writeBuffer)
                    {
                        byte* bptr = b;
                        for (int i = 0; i < newBuffer.Length; i++)
                        {
                             *bptr++ = (byte) newBuffer[i];
                        }
                    }
                }
    
                get
                {
                    char[] newBuffer = new char[BUFFER_SIZE];
    
                    fixed (byte* b = writeBuffer)
                    {
                        byte* bptr = b;
                        for (int i = 0; i < newBuffer.Length; i++)
                        {
                            newBuffer[i] = (char) *bptr++;
                        }
                    }
    
                    return newBuffer;
                }
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                OuterType t = new OuterType();
                System.Console.WriteLine(t);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-07-29
      • 2010-11-05
      • 2011-02-20
      • 2011-08-14
      • 2021-10-18
      • 2015-11-21
      • 2011-06-13
      • 2010-12-06
      • 2011-07-21
      相关资源
      最近更新 更多