Skip to main content
The CallbackArguments class is a container for arguments passed to a C# callback function from Lua. It provides convenient methods for accessing and validating arguments.

Properties

Count
int
Gets the count of arguments.
script.Globals["func"] = new CallbackFunction((ctx, args) => {
    Console.WriteLine($"Received {args.Count} arguments");
    return LuaValue.Nil;
});
IsMethodCall
bool
Gets whether this is a method call (i.e., called with colon ’:’ operator in Lua).When true, the first argument is the ‘self’ object.
script.Globals["obj"] = new CallbackFunction((ctx, args) => {
    if (args.IsMethodCall) {
        var self = args[0]; // The 'self' object
        Console.WriteLine("Called as method");
    }
    return LuaValue.Nil;
});

// Lua: obj:method()  -- IsMethodCall = true
// Lua: obj.method()  -- IsMethodCall = false

Indexer

this[int index]
indexer
Gets the LuaValue at the specified index, or Void if not found.Indices are 0-based (unlike Lua’s 1-based indexing).
script.Globals["add"] = new CallbackFunction((ctx, args) => {
    double a = args[0].Number;  // First argument
    double b = args[1].Number;  // Second argument
    return LuaValue.NewNumber(a + b);
});

Access Methods

RawGet
method
Gets the LuaValue at the specified index, or null if out of bounds.
returns
LuaValue
The value, or null if out of bounds
LuaValue arg = args.RawGet(0, true);
if (arg == null) {
    // Argument not provided
}
GetArray
method
Converts the arguments to an array.
returns
LuaValue[]
Array of arguments
script.Globals["print"] = new CallbackFunction((ctx, args) => {
    LuaValue[] allArgs = args.GetArray();
    foreach (var arg in allArgs) {
        Console.Write(arg.ToPrintString() + "\t");
    }
    Console.WriteLine();
    return LuaValue.Nil;
});
SkipMethodCall
method
Returns a copy of CallbackArguments where the first “self” argument is skipped if this was a method call.
returns
CallbackArguments
Arguments with ‘self’ removed (if method call), otherwise returns itself
script.Globals["method"] = new CallbackFunction((ctx, args) => {
    var actualArgs = args.SkipMethodCall();
    // Now actualArgs[0] is the first real argument,
    // not 'self'
    return LuaValue.Nil;
});

Type Validation Methods

AsType
method
Gets the specified argument as a specific type, throwing an exception if conversion fails.
returns
LuaValue
The validated value
script.Globals["process"] = new CallbackFunction((ctx, args) => {
    var name = args.AsType(0, "process", DataType.String);
    var age = args.AsType(1, "process", DataType.Number);
    var optionalFlag = args.AsType(2, "process", DataType.Boolean, true);
    
    Console.WriteLine($"{name.String}, {age.Number}");
    return LuaValue.Nil;
});
AsInt
method
Gets the specified argument as an integer, throwing if not a valid integer.
returns
int
The integer value
script.Globals["repeat"] = new CallbackFunction((ctx, args) => {
    int times = args.AsInt(0, "repeat");
    string text = args[1].String;
    
    for (int i = 0; i < times; i++) {
        Console.WriteLine(text);
    }
    return LuaValue.Nil;
});
AsOptInt
method
Gets the specified argument as an optional integer.
returns
int?
The integer value, or null if nil
script.Globals["substring"] = new CallbackFunction((ctx, args) => {
    string str = args[0].String;
    int? start = args.AsOptInt(1, "substring");
    int? length = args.AsOptInt(2, "substring");
    
    if (start.HasValue) {
        str = str.Substring(start.Value, length ?? str.Length - start.Value);
    }
    
    return LuaValue.NewString(str);
});
AsOptBoolean
method
Gets the specified argument as an optional boolean.
returns
bool?
The boolean value, or null if nil
script.Globals["log"] = new CallbackFunction((ctx, args) => {
    string message = args[0].String;
    bool? verbose = args.AsOptBoolean(1, "log");
    
    if (verbose ?? false) {
        Console.WriteLine($"[VERBOSE] {message}");
    } else {
        Console.WriteLine(message);
    }
    return LuaValue.Nil;
});
AsUserData<T>
method
Gets the specified argument as userdata of a specific CLR type.
returns
T
The unwrapped CLR object
public class Player {
    public string Name { get; set; }
}

UserData.RegisterType<Player>();

script.Globals["greetPlayer"] = new CallbackFunction((ctx, args) => {
    Player player = args.AsUserData<Player>(0, "greetPlayer");
    return LuaValue.NewString($"Hello, {player.Name}!");
});
AsStringUsingMeta
method
Gets the specified argument as a string, calling the __tostring metamethod if needed.
returns
string
The string representation
script.Globals["print"] = new CallbackFunction((ctx, args) => {
    for (int i = 0; i < args.Count; i++) {
        string str = args.AsStringUsingMeta(ctx, i, "print");
        Console.Write(str + "\t");
    }
    Console.WriteLine();
    return LuaValue.Nil;
});

Example Usage

Basic Argument Access

using SolarSharp.Interpreter;
using SolarSharp.Interpreter.DataTypes;

var script = new Script();

script.Globals["add"] = new CallbackFunction((ctx, args) => {
    // Check argument count
    if (args.Count < 2) {
        throw new ArgumentException("add requires 2 arguments");
    }
    
    // Access arguments by index
    double a = args[0].Number;
    double b = args[1].Number;
    
    return LuaValue.NewNumber(a + b);
}, "add");

script.DoString("print(add(10, 20))"); // 30

Type Validation

script.Globals["formatUser"] = new CallbackFunction((ctx, args) => {
    // Validate argument types
    var name = args.AsType(0, "formatUser", DataType.String);
    var age = args.AsType(1, "formatUser", DataType.Number);
    var active = args.AsType(2, "formatUser", DataType.Boolean, allowNil: true);
    
    bool isActive = active.IsNil() ? true : active.Boolean;
    
    return LuaValue.NewString(
        $"{name.String} ({age.Number}) - {(isActive ? "Active" : "Inactive")}"
    );
}, "formatUser");

script.DoString(@"
    print(formatUser('Alice', 30))           -- Alice (30) - Active
    print(formatUser('Bob', 25, false))      -- Bob (25) - Inactive
");

Variadic Arguments

script.Globals["sum"] = new CallbackFunction((ctx, args) => {
    double total = 0;
    
    for (int i = 0; i < args.Count; i++) {
        total += args[i].Number;
    }
    
    return LuaValue.NewNumber(total);
}, "sum");

script.DoString(@"
    print(sum(1, 2, 3, 4, 5))  -- 15
    print(sum(10, 20))         -- 30
");

Method Call Handling

public class GameObject {
    public string Name { get; set; }
    
    public void SetName(string name) {
        Name = name;
    }
}

UserData.RegisterType<GameObject>();

script.Globals["customMethod"] = new CallbackFunction((ctx, args) => {
    if (args.IsMethodCall) {
        var self = args.AsUserData<GameObject>(0, "customMethod");
        var newName = args[1].String;
        self.Name = newName;
        Console.WriteLine($"Method call on {self.Name}");
    } else {
        Console.WriteLine("Function call (no self)");
    }
    return LuaValue.Nil;
});

var obj = new GameObject { Name = "Object1" };
script.Globals["obj"] = obj;

script.DoString(@"
    obj:customMethod('NewName')   -- Method call
    customMethod('Test')          -- Function call
");

Optional Arguments

script.Globals["greet"] = new CallbackFunction((ctx, args) => {
    string name = args[0].String;
    
    // Optional greeting prefix
    string prefix = args.Count > 1 && args[1].IsNotNil() 
        ? args[1].String 
        : "Hello";
    
    return LuaValue.NewString($"{prefix}, {name}!");
}, "greet");

script.DoString(@"
    print(greet('Alice'))              -- Hello, Alice!
    print(greet('Bob', 'Hi'))          -- Hi, Bob!
    print(greet('Charlie', 'Welcome')) -- Welcome, Charlie!
");

UserData Arguments

public class Vector3 {
    public double X, Y, Z;
    
    public Vector3(double x, double y, double z) {
        X = x; Y = y; Z = z;
    }
}

UserData.RegisterType<Vector3>();

script.Globals["distance"] = new CallbackFunction((ctx, args) => {
    var v1 = args.AsUserData<Vector3>(0, "distance");
    var v2 = args.AsUserData<Vector3>(1, "distance");
    
    double dx = v2.X - v1.X;
    double dy = v2.Y - v1.Y;
    double dz = v2.Z - v1.Z;
    
    return LuaValue.NewNumber(Math.Sqrt(dx*dx + dy*dy + dz*dz));
}, "distance");

script.Globals["v1"] = new Vector3(0, 0, 0);
script.Globals["v2"] = new Vector3(3, 4, 0);

script.DoString("print(distance(v1, v2))"); // 5

Converting to Array

script.Globals["printAll"] = new CallbackFunction((ctx, args) => {
    LuaValue[] allArgs = args.GetArray();
    
    Console.WriteLine($"Received {allArgs.Length} arguments:");
    foreach (var arg in allArgs) {
        Console.WriteLine($"  {arg.Type}: {arg}");
    }
    
    return LuaValue.Nil;
}, "printAll");

script.DoString("printAll(1, 'two', true, {x=10})");

Skipping Self in Method Calls

script.Globals["instanceMethod"] = new CallbackFunction((ctx, args) => {
    // Skip 'self' if method call
    var actualArgs = args.SkipMethodCall();
    
    Console.WriteLine($"Processing {actualArgs.Count} arguments");
    
    for (int i = 0; i < actualArgs.Count; i++) {
        Console.WriteLine($"  Arg {i}: {actualArgs[i]}");
    }
    
    return LuaValue.Nil;
});

script.DoString(@"
    local obj = {}
    obj.method = instanceMethod
    
    obj:method(1, 2, 3)  -- Skips 'obj', processes 1,2,3
    obj.method(1, 2, 3)  -- Doesn't skip, processes 1,2,3
");

Error Handling

script.Globals["safeDivide"] = new CallbackFunction((ctx, args) => {
    try {
        double a = args.AsType(0, "safeDivide", DataType.Number).Number;
        double b = args.AsType(1, "safeDivide", DataType.Number).Number;
        
        if (b == 0) {
            throw new ScriptRuntimeException("Division by zero");
        }
        
        return LuaValue.NewNumber(a / b);
    } catch (ScriptRuntimeException) {
        throw; // Rethrow Lua exceptions
    } catch (Exception ex) {
        throw new ScriptRuntimeException(
            $"Error in safeDivide: {ex.Message}"
        );
    }
}, "safeDivide");

Build docs developers (and LLMs) love