15f509b Jan 18, 2017
@neolithos @halex2005
263 lines (193 sloc) 7.01 KB

clr Library (gateway to .net)

Getting started

clr is a package to access .net framework types and namespaces. The package is implemented in dynamic class LuaType, binds the calls to the host application or the .net framework classes.

NeoLua supports static methods, instance methods, overloaded methods, constructors, properties, types, sub types or events.

local sys = clr.System;

Creates a dynamic type instance for the namespance system and assigns this new type to the local variable sys. So the both following calls are equalent.

local StringBuilder = sys.Text.StringBuilder;
local StringBuilder1 = clr.System.Text.StringBuilder; -- same result

Use this variable like the using in .net to access the type StringBuilder.

To create a new object from the type call the type like a function. This will invoke the constructor.

local sb = StringBuilder('text');
local sb1 = clr.System.Text.StringBuilder('test'); -- same result

If the type is generic use the index access to create a non generic class first.

local lstObject = clr.System.Collections.Generic.List[clr.System.Object]();
local ListGeneric = clr.System.Collections.Generic.List;
local lstString = ListGeneric[clr.System.String]();
local ListStringType = ListGeneric[clr.System.String];
local lstString2 = ListStringType();

But the best way to short cut types is to use the const keyword. Because it creates no runtime overhead, it is only known during compile time. Becareful, no clr is needed.

const ListOfObjects typeof System.Collections.Generic.List[System.Object];

The clr package is a "build in" package, so it is useable in Lua-Lambda's.

using (Lua l = new Lua())
  var f = l.CreateLambda<Func<double, double>>("f", "return clr.System.Math:Abs(x) * 2", "x");
  Console.WriteLine("f({0}) = {1}", 2, f(2));
  Console.WriteLine("f({0}) = {1}", -2, f(-2));

  var f2 = l.CreateLambda("f2", "local Math = clr.System.Math; return Math:Abs(x) * 2;", 
     null, typeof(double), new KeyValuePair<string, Type>("x", typeof(double)));
  Console.WriteLine("f2({0}) = {1}", 2, f2.DynamicInvoke(2));
  Console.WriteLine("f2({0}) = {1}", -2, f2.DynamicInvoke(-2));
Example for StringBuilder
local sys = clr.System;
local sb = sys.Text.StringBuilder();
sb:Append('Hallo '):Append('Welt!');
return sb:ToString();

If you combine this with explict typing, the parser will emit no dynamic calls at all and the runtime of this script is the same like e.g. a C# method.

const StringBuilder typeof System.Text.StringBuilder();
local sb : StringBuilder = StringBuilder();
sb:Append('Hallo '):Append('Welt!');
return sb:ToString();
Example to work with List's
local lst = clr.System.Collections.Generic.List[clr.System.Object]();


foreach a in lst do

for i = 0,lst.Count-1,1 do
  print(i .. ": " .. lst[i]);

return lst.Count;

Static methods/Contructors

Call static methods or contructors direct on the type with the member call. It is also possible to get the reference to the member.

Example static method
const Env typeof System.Environment;
local getEnv = Env.GetEnvironmentVariable; -- creates a new object
Example contructors
const StringBuilder typeof System.Text.StringBuilder;
local sb : StringBuilder = StringBuilder("Hello ");

Instance methods

Methods can also called through a member call or you can get the reference to the member.

const StringBuilder typeof System.Text.StringBuilder;
local sb : StringBuilder = StringBuilder("Hello ");
local app = sb.Append; -- create a new object
app(" What's");
app(" up");

Overloaded methods

If you call a method with overloads throw the member call NeoLua will try to find the correct overload by the given parameters.

console.WriteLine("Number: {0}", 23); -- chooses the (string, arg) overload

If you assign the member of overloaded method to a variable a object will be created.

local writeLine = console.WriteLine; -- create a object for the method
writeLine("Number: {0}", 23); -- chooses the (string, arg) overload
local writeLine1 = writeLine[clr.System.String, clr.System.Object]; -- create a reference to a special overload
writeLine1("Number: {0}", 23);


Access static properties:
Access properties:
const StringBuilder typeof System.Text.StringBuilder;
local sb : StringBuilder = StringBuilder("Hello ");
return sb.Length;


To get the .net type you have tree ways.

Call the virtual GetType method
return clr.System.Environment:GetType();
Use a explicit cast
return cast(type, clr.System.Environment);
.net GetType of the instance
const StringBuilder typeof System.Text.StringBuilder;
local sb = StringBuilder("Hello ");
return sb:GetType();


The example creates a array with the specified length.

local a : int[] = clr.System.Int32[3]; -- int is not a identifier
a[0] = 23;
a[1] = 42;
a[2] = 256;
return a[0] + a[1] + a[2];

The example creates a array with the values.

const int typeof System.Int32; -- declare the identifier
local a : int[] = int[](23, 42, 256);
return a[0] + a[1] + a[2];

Also multidimensional array's are supported. Because of limitations of the array syntax in lua it is not possible to build a type by syntax. It is only possible to access the array with dynamic calls.

const int typeof System.Int32; -- declare the identifier
local a = int[2, 2];
a[0,0] = 23;
a[1,1] = 42;
return a[0,0] + a[1,1];

Becareful, .net array's are zero based, and lua table indexes are one based.


Events get two new virtual members to add or remove methods of the event. It is important, that the signatures must fit together.

public class MyClass
  public event Action EventTest;
myClass.EventTest:add(function() : void print('test'); end);

Extension methods

If you want use extension methods on the clr types in a lua script, you have to register them. They are not resolved automaticly due performance reasons.

To call the lua sub method I registered the string library first.


That is the reason why you can call

return "test":sub(2, 2);

in a script. So, it is possible to extent .net types/classes for a lua a script.

It is also possible to call this function from a lua script.