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));
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();
local lst = clr.System.Collections.Generic.List[clr.System.Object]();
lst:Add(1);
lst:Add(2);
lst:Add("String");
print("Enum:");
foreach a in lst do
print(a);
end;
print("Index:");
for i = 0,lst.Count-1,1 do
print(i .. ": " .. lst[i]);
end;
return lst.Count;
Call static methods or contructors direct on the type with the member call. It is also possible to get the reference to the member.
const Env typeof System.Environment;
print(Env:GetEnvironmentVariable("TEMP"));
local getEnv = Env.GetEnvironmentVariable; -- creates a new object
print(getEnv("TEMP"));
const StringBuilder typeof System.Text.StringBuilder;
local sb : StringBuilder = StringBuilder("Hello ");
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 ");
sb:Append("World!");
print(sb:ToString());
local app = sb.Append; -- create a new object
app(" What's");
app(" up");
app("!");
print(sb.ToString());
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);
print(clr.System.Environment.MachineName);
const StringBuilder typeof System.Text.StringBuilder;
local sb : StringBuilder = StringBuilder("Hello ");
return sb.Length;
To get the .net type you have tree ways.
return clr.System.Environment:GetType();
return cast(type, clr.System.Environment);
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);
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.
LuaType.RegisterTypeExtension(typeof(LuaLibraryString));
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.
clr.Neo.IronLua.LuaType:RegisterTypeExtension(clr.Some.Extension.Type);