Skip to content

.NET Bindings

Kevin Zhao edited this page Jan 25, 2018 · 4 revisions

.NET Bindings

Bindings to .NET objects are achieved using userdata with metamethods that emulate typical C# access of the .NET object. Objects are stored using strong GCHandles, preventing the .NET GC from garbage collecting them. This approach obviates the need of assigning each object a unique ID and performing a lookup for every access.

All bindings are wrapped in a function which reports errors using the error function instead of P/Invoking the luaL_error function! The issue with P/Invoking luaL_error is that this calls longjmp, which can have undefined results on the stack.

Member Access

The members of a .NET object or static members of a .NET type are accessed using the __index metamethod. In particular, methods will return a function which can be called at a later time, events will return a wrapper object that handles event subscription and unsubscription, and indexed properties will return a wrapper object that handles getting and setting. These special results are cached to improve performance.

One-dimensional arrays are also accessed in the same manner. Multi-dimensional arrays must be accessed using the GetValue function.

Method Calls

Method calls will ignore out parameters for the purpose of input validation, and will return ref and out parameters as extra return values. Triton's overload resolution will always handle the following case correctly by picking the method with the least number of default values applied:

void Method(int a);
void Method(int a, int b = 0);

Generic Method Calls

Generic method calls are handled in the failure condition of a normal method call. Another function will be returned, treating the original arguments as type arguments. However, this method of supporting generic methods leads to the following problem:

void Method<T>();
void Method(Type t);

The first method can never be called, because a single type argument is valid for the second method. However, this edge case is rare, and can be resolved by using reflection from within Lua.

Member Modification

The members of a .NET object or static members of a .NET type are modified using the __newindex metamethod.

One-dimensional arrays are also modified in the same manner. Multi-dimensional arrays must be modified using the SetValue function.

Calling

If a .NET object is a Delegate, then it can be called using the __call metamethod. A .NET type can also be instantiated using the __call metamethod, and if it is a generic type, then it will return a constructed generic type which can then be instantiated.

Operators

The operators of a .NET object are implemented using the various operator metamethods. In particular, both operands of a binary operation will be checked for an operator that satisfies the given signature! This deals with the following edge case:

public class Test1 {
    public static int operator +(Test2 t2, Test1 t1);
}
public class Test2 {
    public static int operator +(Test2 t2, int i);
}

lua["t1"] = new Test1();
lua["t2"] = new Test2();
lua.DoString("x = t2 + t1");