【问题标题】:Do I need to pin a struct when copying bytes from the memory location从内存位置复制字节时是否需要固定结构
【发布时间】:2011-04-19 23:52:19
【问题描述】:

我在 C# 中定义了一个结构来镜像本机数据结构,并使用了 Sequential 的 StructLayout。要将结构转换为 Socket IOControl 方法所需的 12 字节(3x 4 字节),我使用 Marshal.Copy 将字节复制到数组中。

由于结构只包含值类型,我是否需要在执行复制之前固定结构?我知道 GC 会压缩堆,因此引用类型的内存地址可以在 GC 期间更改。堆栈分配的值类型也一样吗?

包含pin操作的当前版本如下:

[StructLayout(LayoutKind.Sequential, Pack = 1)]  
struct TcpKeepAliveConfiguration
{
        public uint DoUseTcpKeepAlives;
        public uint IdleTimeMilliseconds;
        public uint KeepAlivePacketInterval;

        public byte[] ToByteArray()
        {
            byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
            GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
            try
            {
                Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
                return bytes;
            }
            finally
            {
                pinStructure.Free();
            }
        }
    }

有什么想法吗?

【问题讨论】:

    标签: c# .net garbage-collection


    【解决方案1】:

    如果您的结构被 lambda 表达式捕获,它不会存储在堆栈中

    因此,我建议您在复制之前始终固定结构。

    Eric Lippert 写了一封您可能感兴趣的 article about value type storage

    【讨论】:

    • 当然,我没有想到那种情况。谢谢弗雷德里克
    • 当它是一个类的成员时,它也在堆上。有时最好将 struct 复制到堆栈,然后通过 '&' 获取它的地址而不进行固定。这取决于结构的大小。
    【解决方案2】:

    Frédéric 和 Aliostad 是正确的;您不知道“this”实际存在于何处,因此您不知道是否允许垃圾收集器移动它。

    我只想指出,对于您的问题,有一个等效的解决方案可能对您有用。您还可以通过以下方式解决您的问题:

    public byte[] ToByteArray()
    {
      byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
      unsafe
      {
        fixed (TcpKeepAliveConfiguration* ptr = &this)
        {
          // now you have pinned "this" and obtained a pointer to it in one step
        }
      }
    }
    

    “固定”语句确保在其块的主体期间,指向“this”的非托管指针是有效的,因为垃圾收集器无法移动内存。基本上这是编写代码的另一种方式;有些人觉得这种方式更容易阅读。

    (请注意,在构建包含不安全上下文的代码时,您必须选中 Visual Studio 中的“允许不安全”复选框或在命令行中使用“/unsafe”标志。)

    【讨论】:

    • 埃里克,感谢您的贡献。 fixed 当您克服看到unsafe 时的不安感觉时,当然会更好地阅读:-) 再次感谢您提供的信息。
    【解决方案3】:

    将定义改为class而不是struct

    是的,您必须这样做 - 您的代码对我来说看起来不错。

    您将不知道您的结构将存在于何处。它可能是另一个对象结构的一部分,因此位于堆上,也可能是最有可能位于堆栈上的局部变量。如果它在堆上,那么您需要将其固定。

    【讨论】:

    • 感谢您的回复,如果我更改为类,那么 pin 是有意义的,因为它将被堆分配。但是,我不需要课程,结构可以正常工作。我的问题是使用结构时是否需要固定。我知道在使用课程时我必须固定。
    • 感谢 Aliostad,Frédéric 也提到了那个场景。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 2018-12-14
    • 2021-08-20
    相关资源
    最近更新 更多