A static class emits a Lua table under the root table. Static methods use dot syntax.
public static class Game
{
public static int Score;
public static void AddScore(int amount)
{
Score += amount;
}
}SF__ = SF__ or {}
SF__.Game = SF__.Game or {}
SF__.Game.Score = 0
function SF__.Game.AddScore(amount)
SF__.Game.Score = (SF__.Game.Score + amount)
endInstance methods use colon syntax. Constructors emit .New(...) and return self.
Constructor chaining with : this(args) emits a call to the current type's generated __Init... function before the chained constructor body. The chained constructor performs instance field initialization, so the outer constructor does not re-run field initializers.
Object initializer assignments run after construction and return the initialized object. This includes generic construction such as new T { Owner = this }, which lowers to a temporary object, assignment statements, and a final return of that temporary.
public class Hero
{
public string Name;
public int Health;
public Hero(string name, int health)
{
Name = name;
Health = health;
}
public void TakeDamage(int amount)
{
Health -= amount;
}
}SF__.Hero = SF__.Hero or {}
function SF__.Hero.New(name, health)
local self = setmetatable({}, { __index = SF__.Hero })
self.Name = name
self.Health = health
return self
end
function SF__.Hero:TakeDamage(amount)
self.Health = (self.Health - amount)
endvar h = new Hero("Arthas", 100);
h.TakeDamage(10);local h = SF__.Hero.New("Arthas", 100)
h:TakeDamage(10)Instance fields are self.Field. Static fields and const fields are SF__.Type.Field. Static field initializers run after the type table and methods are registered, so they can safely call current-type helpers such as .New().
Auto-properties lower to fields. Getters and setters are not emitted as methods.
public int Health { get; set; }Lowers the same as public int Health;.
Field-like events lower to a delegate field. Raise and subscribe use the field directly.
Overloaded methods and constructors get simplified parameter-signature suffixes to avoid Lua name collisions. Primitive signatures use short codes such as i for integer, f for float, d for double, b for bool, and s for string; named types use a lowercase sanitized type name.
public string Add(int a, int b) => "ints";
public string Add(string a, string b) => "strings";
var ints = Add(1, 2);
var strings = Add("a", "b");function SF__.Demo.Add__ii(a, b)
return "ints"
end
function SF__.Demo.Add__ss(a, b)
return "strings"
end
local ints = SF__.Demo.Add__ii(1, 2)
local strings = SF__.Demo.Add__ss("a", "b")Generic method type arguments lower to hidden leading Lua parameters that carry the runtime type table. This supports type checks against the type parameter and new T() for class types with a new() constraint.
public T AddComponent<T>() where T : Component, new()
{
var component = new T();
return component;
}
var transform = AddComponent<Transform>();function SF__.GameObject:AddComponent(T)
local component = T.New()
return component
end
local transform = self:AddComponent(SF__.Transform)Optional parameter defaults lower to a nil guard at the start of the Lua function. This preserves explicit false and zero defaults, and lets omitted Lua arguments use the C# default value.
public static int Pick(int first = 1, int second = 2)
{
return first + second;
}
var value = Pick(second: 5);function SF__.Demo.Pick(first, second)
if first == nil then first = 1 end
if second == nil then second = 2 end
return (first + second)
end
local value = SF__.Demo.Pick(nil, 5)Static constructors lower to top-level Lua statements that run after the type's methods are emitted. That keeps current-type helpers such as .New() available before static initialization executes.
Abstract classes (without implementations), partial classes across files that don't compile together, and ref/out parameters produce a transpiler error.