如何在C#类成员变量中持有struct的引用?
在C#中持有外部struct引用的等效实现
你的核心需求是让C#类持有外部框架创建的struct的引用(而非拷贝),以便能访问到该struct的动态更新内容。由于C#中struct是值类型,默认操作会产生拷贝,以下是几种符合需求的实现方式:
1. 不安全代码直接使用指针(最贴近C++逻辑)
这种方式和你的C++代码逻辑几乎一致,需要在项目中开启允许不安全代码编译选项(项目属性→生成→勾选对应选项)。
代码示例
// 外部框架定义的不可修改struct public struct sMyStruct { public int SomeValue; // 其他字段... } public class MyClass { // 持有struct的指针 private unsafe sMyStruct* _refToStruct; // 构造函数接收指针 public unsafe MyClass(sMyStruct* refToStruct) { _refToStruct = refToStruct; } // 访问并修改原struct内容的示例方法 public unsafe void UpdateStructValue(int newValue) { if (_refToStruct != null) { _refToStruct->SomeValue = newValue; } } } // 使用示例 unsafe class Program { static void Main() { // 模拟外部框架创建的非托管struct unsafe { sMyStruct* myStruct = (sMyStruct*)Marshal.AllocHGlobal(sizeof(sMyStruct)); myStruct->SomeValue = 10; MyClass c = new MyClass(myStruct); c.UpdateStructValue(20); // 此时原struct的SomeValue已变为20 // 内存释放由外部框架负责,此处仅为示例 Marshal.FreeHGlobal((IntPtr)myStruct); } } }
注意事项
- 必须确保外部struct的内存生命周期由框架或你正确管理,避免野指针问题。
- 仅限 blittable 类型的struct(可直接在托管/非托管内存间转换)使用。
2. 使用Memory<T>/Span<T>(安全的托管内存引用)
Span<T>是栈分配的内存引用类型,无法直接作为类字段存储;Memory<T>是堆兼容的内存包装类型,可长期持有,需要时转换为Span<T>访问内容,全程无需unsafe代码。
代码示例
public struct sMyStruct { public int SomeValue; } public class MyClass { // 用Memory<T>长期持有内存引用 private Memory<sMyStruct> _structMemory; public MyClass(Memory<sMyStruct> structMemory) { _structMemory = structMemory; } public void UpdateStructValue(int newValue) { // 转换为Span<T>访问原内存内容 Span<sMyStruct> structSpan = _structMemory.Span; if (!structSpan.IsEmpty) { structSpan[0].SomeValue = newValue; } } } // 使用示例 class Program { static void Main() { // 模拟外部框架提供的托管内存中的struct sMyStruct[] externalStructArray = new sMyStruct[1]; externalStructArray[0].SomeValue = 10; // 从数组创建Memory<T>指向目标struct Memory<sMyStruct> structMemory = externalStructArray.AsMemory(0, 1); MyClass c = new MyClass(structMemory); c.UpdateStructValue(20); // 原struct的内容已更新 Console.WriteLine(externalStructArray[0].SomeValue); // 输出20 } }
注意事项
- 适用于托管内存中的struct,若外部框架提供非托管内存,可配合
Marshal类创建Memory<T>引用。 - 由CLR管理内存,类型安全,无野指针风险。
3. 使用IntPtr(非托管内存的通用安全方式)
如果外部框架创建的是非托管内存中的struct,可通过IntPtr保存内存地址,借助Marshal类读写内容,全程无需unsafe代码。
代码示例
public struct sMyStruct { public int SomeValue; } public class MyClass { private IntPtr _structPtr; public MyClass(IntPtr structPtr) { _structPtr = structPtr; } public void UpdateStructValue(int newValue) { if (_structPtr != IntPtr.Zero) { // 读取原struct内容 sMyStruct tempStruct = Marshal.PtrToStructure<sMyStruct>(_structPtr); tempStruct.SomeValue = newValue; // 将修改后的内容写回原内存 Marshal.StructureToPtr(tempStruct, _structPtr, false); } } public int GetStructValue() { return _structPtr != IntPtr.Zero ? Marshal.PtrToStructure<sMyStruct>(_structPtr).SomeValue : default; } } // 使用示例 class Program { static void Main() { // 模拟外部框架分配非托管内存并初始化struct IntPtr structPtr = Marshal.AllocHGlobal(sizeof(sMyStruct)); sMyStruct initialStruct = new sMyStruct { SomeValue = 10 }; Marshal.StructureToPtr(initialStruct, structPtr, false); MyClass c = new MyClass(structPtr); c.UpdateStructValue(20); Console.WriteLine(c.GetStructValue()); // 输出20 // 内存释放由外部框架负责,此处仅为示例 Marshal.FreeHGlobal(structPtr); } }
注意事项
- 每次读写会产生struct拷贝,效率略低于指针或Span方式,但完全安全。
- 非 blittable 类型的struct需添加
[StructLayout]属性指定内存布局。
方案选择建议
- 追求和C++一致的指针逻辑且能开启unsafe代码:选方案1
- 托管内存场景下需要安全的长期引用:选方案2
- 非托管内存场景且不想用unsafe代码:选方案3
内容的提问来源于stack exchange,提问作者user3161924
相关产品推荐
相关产品推荐

