Class / Interface Marshal
Normative: Default Marshal rules for reference types (
class,interface,string, array instances, delegate instances, etc.) between Lua and C#. Related: Type tables and member access →../02-TYPE-SYSTEM.md;ref/out/inoverview →03-BYREF.md;[LuaMarshalAs]→02-MARSHAL-AS.md;zlua.cast→../05-LIB.md.
Platform principle: Mono and Il2Cpp share the same Lua-visible semantics; class instances default to GCHandle + full userdata (Il2Cpp: ObjectRegistry + Il2CppObject*).
1. Default Marshal (summary)
Without [LuaMarshalAs], reference types follow the overview matrix in 01-OVERVIEW.md; this section adds class / interface semantics.
| Direction | Lua shape | Notes |
|---|---|---|
| C# → Lua | ClassUserData (ByObj full userdata) | Reference identity; IMT façade = declared type (see §2) |
| Lua → C# | ClassUserData or nil | Must be assignable to target declared type; nil ↔ null |
string | Lua string or nil | Only when declared type is string; declared as object → still Object userdata |
interface | Same as class | Façade = interface declared type (not implementing class); see §2, §4 |
| Array | ByObjUserData | See 07-ARRAY.md |
| Delegate | function or DelegateUserData | See 09-FUNCTION.md |
UserData shape: ClassUserData is full userdata from lua_newuserdata + instance metatable IMT; scripts access members via : / .. Differs from OpaqueValue in 04-OPAQUE.md (lightuserdata, no metatable) and Pointer in 10-POINTER.md.
Field, method, and static member access details: ../02-TYPE-SYSTEM.md and ../metatable/.
2. Declared-type façade (View vs actual type)
For all reference type parameters, return values, and fields/properties (class / interface / object / arrays / delegates, etc.), Marshal distinguishes Identity and View:
| Concept | Meaning |
|---|---|
| Identity | Managed object reference in userdata payload (runtime actual instance) |
| View / façade | IMT and member visibility on userdata; sole source = declared type of this Marshal |
2.1 Rules
- C# → Lua: Always choose default marshal shape and ByObj IMT by declared type; do not reattach a more specific runtime type's mt, and do not switch to special Marshal such as
string(e.g. astringinstance on anobjectparameter remains Object userdata, not Lua string). - Value types: No inheritance-façade issue; follow
05-STRUCT.mdetc. - Virtual methods: Member lookup uses
MethodInfoon the declared type; call does vtable dispatch on the realthis(overridestill lands on the implementing class). - Non-virtual /
newhide: Use declared-type slot (callingnew-hiddenBarviaBasefaçade yieldsBase.Bar). - Downcast: Only
zlua.cast(IsAssignableFrom(targetType, obj.klass)); returns new userdata (same identity, new façade). - Object cache: Key is
(identity, viewType); one instance may have multiple view userdata.
2.2 Example
Base CreateChild() => new Child();
local o = ObjectFactory.CreateChild() -- façade Base: Child.y invisible; new Bar → Base.Bar
local c = zlua.cast(o, Child) -- façade Child (requires IsAssignableFrom)
Virtual methods look up MethodInfo via Base façade, then virtual-dispatch on the real instance.
2.3 object parameters
| Item | Rules |
|---|---|
| Push | ClassUserData, façade System.Object |
| Pop | Accepts boolean / number / string / userdata |
| Runtime type | Does not rewrite Marshal by runtime type (runtime string stays Object userdata) |
For Nullable<T> where T is a reference type, null ↔ nil; when present, same class rules as T. See 01-OVERVIEW.md.
3. Table / UnpackedValues (not for class / interface)
class and interface disallow [LuaMarshalAs(Table)] / [LuaMarshalAs(UnpackedValues)] (legal set: 02-MARSHAL-AS.md §3). Reference types keep default ByObjUserData and member access.
On misuse: Mono error-logs and falls back to Default; Il2Cpp Generate / XML may hard-fail.
4. Interface Marshal
| Item | Rules |
|---|---|
| Default C# ↔ Lua | ByObjUserData (ClassUserData); façade = interface declared type (not implementing class) |
nil | ↔ null |
[LuaMarshalAs] | UserData, OpaqueValue (no Table / UnpackedValues) |
| Member access (default userdata) | Only members visible on the interface + inherited interfaces; implementing-class-only members invisible |
5. ref / out / in reference-type parameters (Lua → C#)
Overview: 03-BYREF.md. Reference types (including string) points:
| Lua argument | Behavior |
|---|---|
| OpaqueValue (compatible with A) | Pass handle address (can write back to original slot) |
ByObjUserData / Lua string (only when A is string) / nil / other Pop-able shapes | Obtain managed pointer (or null) → write stack temporary → pass temp address |
5.1 Write-back: temp slot ⇒ no rebind
| C# operation | Lua side (temp-slot path) |
|---|---|
refParam = otherObject (rebind) | Not visible |
| In-place mutation of a mutable object | Visible (same managed object) |
Assign new string to ref string | Not visible (same as rebind) |
local s = "hello"
CS.Demo.ChangeString(s) -- void ChangeString(ref string s) { s = "world"; }
-- s is still "hello"
local sb = StringBuilder("hi")
CS.Demo.Append(sb, "!") -- shared reference; content mutable
5.2 C# → Lua
ref/out/in default Push OpaqueValue; see 04-OPAQUE.md.
6. string Marshal notes
| Declared type | C# → Lua | Lua → C# |
|---|---|---|
string | Lua string | Lua string or nil |
object (runtime string) | Object userdata (façade System.Object) | object Pop rules |
[LuaMarshalAs(UserData)] on string | ByObjUserData (managed System.String object) | Force userdata path |
[LuaMarshalAs(Bytes)] is for byte[] ↔ Lua string, not System.String; see 07-ARRAY.md.
7. Mono / Il2Cpp consistency
| Item | Requirement |
|---|---|
| Default Push / Pop | ClassUserData; nil ↔ null |
Façade (identity, viewType) | Same on both platforms |
zlua.cast | Same identity, new view |
| C#→Lua byref | OpaqueValue (04-OPAQUE.md) |
| Error messages | Same or equivalent |
8. Related docs
| Doc | Contents |
|---|---|
01-OVERVIEW.md | Default Marshal matrix |
03-BYREF.md | ref / in / out overview |
04-OPAQUE.md | C#→Lua byref default OpaqueValue |
07-ARRAY.md | Array ByObjUserData |
09-FUNCTION.md | Delegate Marshal |
../02-TYPE-SYSTEM.md | Type tables, IMT, member access |
../05-LIB.md | cast, box |