Skip to main content

Lua calling C#

Access public types and members through the global CSharp root table. Semantics stay close to C#: Type() constructs, obj:Method() calls instance methods, statics go through the type table. Namespaced types need bracket keys.

Canonical: app.lua, Demo.cs

Resolving same-name multiple signatures: Method overloads (preview at the end of this chapter).

1. Accessing types

CSharp
└─ Assembly-CSharp
└─ Demo
└─ ['MyGame.UI.Panel']
CSharp['AC'] = CSharp['Assembly-CSharp'] -- 推荐短别名

local Demo = CSharp.AC.Demo
local Panel = CSharp.AC['MyGame.UI.Panel']
local Nested = CSharp.AC['Outer+Nested'] -- 嵌套类型用 +
TypeLua access
No namespaceCSharp.AC.Demo
With namespaceCSharp.AC['MyGame.UI.Panel']
NestedCSharp.AC['Outer+Nested']
BCLCSharp.mscorlib['System.Int32'], etc.
warning

Namespaced types with dots cannot be opened as CSharp.AC.MyGame.UI.Panel with chained dots; use a string key.

  • Types are lazy-loaded; only public is visible
  • Static and instance metadata are separate; do not mix them

2. Static members (fields, properties, methods)

-- 静态方法
print(CSharp.AC.Demo.Add(3, 5))

-- 静态字段 / 无参 Property
CSharp.AC.Demo.s_x = 10
print(CSharp.AC.Demo.GetSX())
C#Lua readLua write
Static field s_xType.s_xType.s_x = v
Static parameterless PropertyType.PropType.Prop = v
Static methodType.Add(a, b)

3. Constructing instances

local demo = CSharp.AC.Demo() -- ≡ new Demo()

Parameterized constructors support default overload dispatch; multiple constructors: Method overloads.

4. Instance members (fields, properties, methods)

local demo = CSharp.AC.Demo()

demo:SetX(10) -- 实例方法(冒号传 self)
print(demo:GetX())

demo.x = 20 -- public 字段与无参 Property 写法相同
print(demo.x)
SyntaxMeaning
demo:GetX()Instance method
demo.xField or parameterless Property
CSharp.AC.Demo.Add(3,5)Static method

Fields and properties

  • Parameterless { get; set; } matches field syntax; on Il2Cpp Player, simple int properties may use an offset fast path—prefer demo.x over GetX() on hot paths
  • Parameterized indexers (this[int]) dispatch as methods; you cannot freely write obj[i] (szarray etc. have special rules — see Arrays)
  • Unregistered members are a strict misserror, with no reflection fallback

Inheritance

Subclass instances can call public instance members from base types; static members go through the declaring type’s type table.

nil and null

  • Lua nil ↔ C# reference-type null and vice versa
  • Value-type structs cannot be nil (unless Nullable<T> — see Value types)

5. Event (add_ / remove_)

ZLua has no Event-specific metatable (no .get / .set / assignment sugar). Use the compiler-generated ordinary methods:

local function onChanged(v)
print("hp", v)
end

host:add_OnHealthChanged(onChanged)
-- ...
host:remove_OnHealthChanged(onChanged) -- 须同一 function 引用

Static events: Type.add_Foo(handler). The handler is a Lua function, implicitly marshaled per Function.

If you see OnX.get is nil, you are still using xLua-style sugar; switch to add_ / remove_.

Full example (from Demo)

local function test_call_static_method()
print("Demo.Add:", CSharp.AC.Demo.Add(3, 5))
end

local function test_call_instance_method()
local demo = CSharp.AC.Demo()
print("Demo:GetX():", demo:GetX())
end

local function test_access_instance_field()
local demo = CSharp.AC.Demo()
demo:SetX(10)
assert(demo.x == 10)
demo.x = 20
assert(demo:GetX() == 20)
end

Method overloads preview

demo:Run(10) and demo:Run("hi") use runtime dispatch when there are multiple overloads. Exact picks use full-signature keys demo['Run(System.Int32)'](demo, 10) (automatic at Bind; no API); short hot-path names use [LuaAlias] or register_method. See Method overloads.

Common mistakes

SymptomFix
type not foundAssembly name, namespace brackets, whether public
static member not foundDo not access statics via instances; use the type table
instance member not foundSpelling / visibility
member not writableRead-only Property
Event .get is nilUse add_ / remove_

Learning path

PreviousEmmyLua debugger
NextC# calling Lua