让我说我在C ++中有以下结构
struct Base
{
USHORT size;
}
struct Inherited : public Base
{
BYTE type;
}
我想在C#中编组Inherited
,但结构继承在C#中不起作用。正在做以下适当的事情吗?
public interface IBase
{
ushort Size { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
public struct Inherited : IBase
{
public ushort Size { get; set; }
public byte Type { get; set; }
}
我在这里简化了问题,结构更大,难以验证结果。此外,结构来自另一个不太好的文档,使得验证结果更加困难。在C ++中使用继承时,是子结构之前还是之后的基类字段?
我使用IBase
作为强制存在基本字段的方法。
不幸的是,我无法控制C ++方面(我集成的外部系统的SDK)。
答案 0 :(得分:2)
"适当的"并不完全适用于这些C#声明。到目前为止,避免事故的最佳方法是不依赖于属性和接口的实现细节。此结构应声明为 internal ,只使用普通字段。
该代码段未演示故障模式,因此我不得不假设它是 出现问题的真实声明的简化版本。检查C#代码是否正确获取结构声明的方法是验证C ++和C#中结构的大小和最后一个字段的偏移量是否相同。首先编写一个小测试程序来检查,这个代码片段的C ++版本应如下所示:
#include <Windows.h>
#include <stddef.h>
struct Base {
USHORT size;
};
struct Inherited : public Base {
BYTE type;
};
int main()
{
int len = sizeof(Inherited);
int ofs = offsetof(Inherited, type);
return 0;
}
在这种情况下,使用调试器检查 len 和 ofs 变量4和2。在C#中做同样的事情:
using System;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
var len = Marshal.SizeOf(typeof(Inherited));
var ofs = Marshal.OffsetOf(typeof(Inherited), "<Type>k__BackingField");
}
}
public interface IBase {
ushort Size { get; set; }
}
[StructLayout(LayoutKind.Sequential)]
public struct Inherited : IBase {
public ushort Size { get; set; }
public byte Type { get; set; }
}
仍然是4和2,所以一个完美的匹配和pinvoke应该是好的。当您在真实声明中出现不匹配时,在ofs
变量上向后工作,您将发现声明错误的成员。请注意使用属性的后果,强制检查支持字段的名称。当强烈建议使用字段而不是属性声明结构时,此代码将更加复杂。
答案 1 :(得分:1)
您正在假设C ++编译器将如何在内存中布置类。取决于您的编译器和&amp;你正在使用什么标志,这可能会改变。还取决于您使用的字段。例如,一些编译器完全合理地对齐这样的对象:
struct Obj
{
char c; // <-- starts at 0 byte
int i; // <-- starts at 4 byte - 4 byte alignment improves performance.
}
因此,您可以看到C ++类可能无法映射到您希望它们在C#中的布局方式。
有控制它的标志 - 您可以将C ++中的打包设置为0,然后在C#中使用顺序布局,然后您的方法是合理的。
您对属性的使用不是主要问题 - 只要您了解 何处将由编译器为您生成隐式支持字段。
答案 2 :(得分:0)
我终于在我试图做的事情中找到了真正的问题。我在SDK中使用的回调函数发送给我struct Base
并通过分析里面的字段确定它是哪种继承类型。然后我必须&#34;演员&#34;基类型到继承类型。这是我最初做的事情:
static T CopyStruct<T>(ref object s1)
{
GCHandle handle = GCHandle.Alloc(s1, GCHandleType.Pinned);
T typedStruct = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return typedStruct;
}
这种做法永远不会超出struct Base
的大小。因此,Inherited
类型的所有额外字段都不能正确初始化(在复制的内存之外读取)。我最终以一种不安全的方式做到如下:
fixed (Base* basePtr= &base)
{
inherited= *(Inherited*) basePtr;
}
这样,inherited
指向原始内存块,可能会在base
大小之外读取。
感谢您之前的所有答案!我实际上构建了一个C ++应用程序来验证我所有C ++模型的大小和偏移量。