跳到主要内容

指针与不支持类型

规范性: 非托管指针、函数指针、以及 v1 默认不支持或受限的 CLR 形态之 Marshal 规则。 相关: 默认矩阵 → 01-OVERVIEW.md;IntPtr 整型规则 → 01-OVERVIEW.md;OpaqueValue 对比 → 04-OPAQUE.md;Delegate 对比 → 09-FUNCTION.md;ref struct → 05-STRUCT.md、../05-LIB.md。

平台原则: Mono 与 Il2Cpp 的 Lua 可见语义一致。


1. 与 IntPtr / UIntPtr 的区分​

§1 总览中 IntPtr / UIntPtr / nint / nuint 走 整型数值 Marshal(ToInt64 / new IntPtr),不是 本节 Pointer。

类型Lua 默认形态脚本可当作整数运算
IntPtr / UIntPtr / nint / nuintinteger / number可以(按数值)
T* / void* 等非托管指针Pointer(lightuserdata)不可以(仅透传)
函数指针 delegate*<…>Pointer(lightuserdata)不可以(仅透传)

2. 非托管指针(T*、void* 等)​

范围: CLR 中 Type.IsPointer == true 且元素为 非托管 类型的指针,例如 int*、byte*、void*、MyStruct*(MyStruct 为 unmanaged struct)。

不包含 IntPtr / UIntPtr(见 §1)。

2.1 默认 Marshal​

方向默认形态说明
C# → LuaPointer(lightuserdata)Push 指针 地址值(uintptr_t / 平台指针宽度);无 metatable
Lua → C#Pointer(lightuserdata)Pop 时须为 Pointer 形态;按声明指针类型还原

2.2 Lua 侧能力(刻意受限)​

允许禁止
作为实参 原样传递 给下一个 C# 调用(同步链内透传)解引用、读写指向内存
与 nil 区分(非 null 指针才有 Pointer): / . 成员访问、算术、#、pairs 等
—写入全局 / 表 / upvalue 后在 异步 或 跨 pcall 使用(地址可能失效)

设计理由: Lua 无法安全表达 C# 非托管指针的生命周期与别名;仅支持 不透明令牌式透传,供 native / 底层 API 衔接。

2.3 [LuaMarshalAs]​

非托管指针允许 Default 与 OpaqueValue(仅 C#→Lua)。UserData、Table 等仍 非法(见 02-MARSHAL-AS.md)。OpaqueValue 与默认 Pointer lightuserdata 不同:走 Opaque 槽与 get_opaquevalue / set_opaquevalue 生命周期规则。


3. 函数指针(function pointer)​

范围: CLR 中 Type.IsFunctionPointer == true 的类型,例如 C# 9+ 的 delegate*<int, int>、delegate*<void>。

3.1 默认 Marshal​

方向默认形态说明
C# → LuaPointer(lightuserdata)Push 函数入口 地址;无 metatable
Lua → C#Pointer(lightuserdata)Pop 还原为对应 function pointer 类型

3.2 Lua 侧能力​

与 §2 相同——仅透传,不能从 Lua 侧 调用 该地址。

3.3 [LuaMarshalAs]​

与 §2.3 相同:允许 Default 与 OpaqueValue(仅 C#→Lua)。

3.4 与 Delegate 对比​

类型Lua 默认形态Lua 侧可调用
Action / Func<…> 等 DelegateDelegateUserData 或 Lua function可以(见 09-FUNCTION.md)
delegate*<…> 函数指针Pointer(lightuserdata)不可以

4. System.TypedReference​

TypedReference 仅 以 OpaqueValue 形态在 C# ↔ Lua 之间传递;默认即为 OpaqueValue,无需再标 [LuaMarshalAs(OpaqueValue)]。其它 marshal 形态(UserData / Table / integer 等)均不支持。

方向规则
C# → Lua默认 Push OpaqueValue(04-OPAQUE.md);脚本经 get_opaquevalue / set_opaquevalue 读写
Lua → C#仅 接受兼容的 OpaqueValue handle(类型校验后绑定地址);其它 Lua 形态 → 错误
其它 [LuaMarshalAs]非法(回退或绑定期拒绝,见 02-MARSHAL-AS.md)

原因: TypedReference 绑定受控栈上的类型化槽位,无法稳定映射为普通 Lua 值或 userdata;OpaqueValue 仅暴露当前调用期内的槽地址,与其语义匹配。


5. 其他不支持或受限类型​

下列类型在 01-OVERVIEW.md 总览中已简要列出;此处集中说明。

5.1 decimal​

方向规则
默认暂不支持
[OpaqueValue](C#→Lua)合法
Pop/Push(Default)未纳入 v1 默认路径

5.2 ref struct(Span<T>、ReadOnlySpan<T> 等)​

方向规则
by-val 形参不能 作为普通默认 marshal
受控路径仅 ref StructUserData / 04-OPAQUE.md OpaqueValue 等

详见 05-STRUCT.md、../05-LIB.md。

5.3 Nullable<T>​

方向规则
有值同 T 的 marshal
nullnil
T 为值类型Pop 接受 nil

见 01-OVERVIEW.md、../02-TYPE-SYSTEM.md §Nullable。

5.4 dynamic​

编译期按 object 处理;无独立 Lua 形态。

5.5 开放泛型形参​

如 void M<T>(T x) 且 T 未实例化:由 调用时类型实参 决定 marshal;见 ../02-TYPE-SYSTEM.md。


6. 注册 / 暴露阶段应拒绝的签名​

以下属于 签名非法(非 marshal 规则细节):

条件行为
ref struct by-val 形参组合拒绝
无法解析的 byref 修饰符 组合拒绝

允许: GetFunction 取得的 delegate 调用与 delegate bridge 上的 ref/out/in(C#→Lua 见 04-OPAQUE.md;Lua→C# 见 03-BYREF.md)。


7. Pointer Pop 细则​

项规则
接受形态仅 Pointer lightuserdata
不 接受integer / number、full userdata、OpaqueValue handle 的隐式互转
null 指针C#→Lua:按实现 Push Pointer 或 nil(须两平台一致);Lua→C#:nil 是否对应 null 指针以实现文档为准
错误类型不匹配 → luaL_error / 等价异常

8. 三种 lightuserdata 对比​

种类用途metatable脚本读写
Pointer(§2、§3)非托管指针 / 函数指针透传无不可解引用
OpaqueValue(04-OPAQUE.md)C# 栈帧参数槽 handle无经 get_opaquevalue / set_opaquevalue
(非 lightuserdata) ClassUserData 等托管对象有 IMT: / . 成员访问

9. Mono / Il2Cpp 一致性​

项要求
Pointer / function pointer Pushlightuserdata,地址宽度 = 平台指针
Pointer Pop仅接受 Pointer;不与 integer / full userdata 隐式互转
TypedReference仅 OpaqueValue(默认即此);语义一致
decimal / ref struct by-val一致的不支持或受限行为
错误消息一致或等价

10. 相关文档​

文档内容
01-OVERVIEW.md默认矩阵、IntPtr
02-MARSHAL-AS.md指针类型合法标注集合
04-OPAQUE.mdOpaqueValue vs Pointer
09-FUNCTION.mdDelegate vs 函数指针
05-STRUCT.mdref struct
../02-TYPE-SYSTEM.mdNullable、特殊类型族