结构体是否会增加实例大小的开销?

9
具体来说,如果我创建一个只有一个字段的结构体,它实际上充当该值的包装器,将这个结构体传递给期望底层类型的P/Invoke方法是否安全?
我正在使用一个本地库,其API涉及许多指向结构体类型的指针,并且我想使用比IntPtr更类型安全的东西来保持它们的所有内容,通过在通用结构体中包装IntPtr。那会行吗?(并且是否已经完成了此操作?)

1
struct 只是一个内存中的 blob,没有其他额外的开销,除了 struct 本身的成员。你甚至可以使用 StructLayoutAttribute 指定在内存中布局 struct 的方式。 - Yuval Itzchakov
你能展示一下你想要修改的签名以及如何修改吗?你是指想要将 SomeFunction(IntPtr foo) 改为 SomeFunction(MyStruct foo) 吗? - CodeCaster
@CodeCaster:ref MyStruct foo 是理想的,但是我从一个返回 MyStruct* 的函数中获取 MyStruct,这个函数来自外部库。 - Mason Wheeler
你还卡住了吗? - David Heffernan
2个回答

7
只要使用LayoutKind.Sequential,则结构体的单个字段将位于偏移量0处。因此,您的假设是正确的。我推测您所说的指向结构体类型是指一个不透明的指针。该结构体被前向声明但从未定义过。然而,在这种情况下,我无法看出在C#代码中声明结构体如何真正帮助您。不透明指针是不透明的。只有库实现知道如何分配它。你不能在使用者里分配它。 更新 也许您想像这样用一个结构体包装一个不透明的指针:
[StructLayout(LayoutKind.Sequential)]
struct FooHandle
{
    IntPtr handle;
}

// and so on for other handle types

与这个结构体的互操作将与使用 IntPtr 的互操作无法区分。

1
由于这可能对 OP 不明显,LayoutKind.Sequential 是结构体的默认值(根据文档:"C#、Visual Basic 和 C++ 编译器默认将顺序布局值应用于结构。"),因此如果没有指定任何内容,则无需添加任何内容。 - user743382
1
你不能在消费者中分配它。我可以从库实现中接收它,并定义PInvoke方法将其返回为TypedPointer<LibraryStruct>,这比IntPtr更容易使用。 - Mason Wheeler
@Mason 我需要猜测一下你的情境和你提出的解决方案是什么样子的。 - David Heffernan
我不明白在C#中声明虚假结构体如何有助于解决您的实际问题,但我认为我回答了您提出的问题。 - David Heffernan
啊,我现在明白了。结构体的成员将是一个单独的IntPtr? - David Heffernan
显示剩余2条评论

7
不要将其作为结构体传递。
不要直接使用P/Invoke方法,而是将其保持为private,并公开将直接接受包装器类型的公共方法 - 并处理您需要执行的任何操作以向前传递它。然后,您的代码仅使用这些公共方法而无需担心不安全的结构 :).
混合不同类型并期望它们神奇地工作是一场灾难。使自己变得明确,并且不依赖于隐藏和隐式强制转换。
最好的事情是它允许您非常轻松地使用某些指针包装器,实际上它是安全的 - 您可以将其保留为引用,如果失去,则可以使用终结器根据需要处置未受控资源。查看如何使用SafeHandle及其后代的示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接