目录
1.Getting Started
步骤1:在 IDE 中引入 MoonSharp
步骤2:引入命名空间
步骤3:调用脚本
步骤4:运行代码
2.Keeping a Script around
步骤1:复现前教程所有操作
步骤2:改为创建Script对象
步骤3:访问全局环境
步骤4:直接调用函数
3.DynValue revealed
步骤1:重新执行你在上一个教程中的所有操作
步骤2:将 fact 函数存入 DynValue
步骤3:将数字存入 DynValue
步骤 4:了解 DataType(s)
步骤 5:元组
4.Calling back C#
步骤 1:永远不要厌倦阶乘
步骤 2:自定义乘法函数
返回一系列数字
返回表格
接收一个表
5.Auto-conversions
自定义转换器
CLR 类型到 MoonSharp 类型的自动转换
MoonSharp 类型到 CLR 类型的标准自动转换
MoonSharp 类型到 CLR 类型的受限自动转换
文档地址:
MoonSharp
1.Getting Started
您的第一个 MoonSharp 项目的快速指南
本教程将带你初步体验 MoonSharp 的简洁与强大。虽然 MoonSharp 有更优的使用方式,但这是最基础的一种。其他教程会深入探讨如何充分发挥 MoonSharp 的潜力。现在,让我们开始吧!
关于语言支持的说明
本网站大多数教程仅提供 C# 示例,但本页会为 VB.NET 用户提供一些指引。
MoonSharp 兼容所有 CLR 语言(C#、VB.NET、C++/CLI、F#、Boo 等),理论上也支持DLR语言(如 IronPython、IronRuby)。但由于维护多语言示例的工作量较大,后续教程仅提供 C# 示例(本入门页含 VB.NET)。
大多数教程的代码可在 GitHub 的示例项目中找到。
学习前提:需熟悉Lua和至少一门.NET语言(推荐 C#),否则可能难以理解示例。
步骤1:在 IDE 中引入 MoonSharp
根据使用的IDE(Visual Studio、MonoDevelop、SharpDevelop、Unity)选择以下方式:
Visual Studio(通过 NuGet)
1.在包管理器控制台输入:
PM> Install-Package MoonSharp
2.或右键“引用”->“管理NuGet包”->搜索“MoonSharp”并安装。
Xamarin Studio(通过NuGet)
菜单栏选择“项目”->“添加NuGet包”->搜索“MoonSharp”并安装。
其他IDE(手动添加)
将 MoonSharp 发行包中对应平台的 MoonSharp.Interpreter.dll 添加为项目依赖。
Unity
1.推荐方式:通过Asset Store安装(待审核后在此链接提供)。
2.手动方式:
将 interpreter/net35 目录下的 MoonSharp.Interpreter.dll 放入 Assets/Plugins。
若需支持 Windows Store/WP:
将 interpreter/portable-net40 下的 DLL放入 Assets/Plugins/WSA。
参考此指南配置。链接
3.IL2CPP项目:
在 Assets 目录下创建/编辑 link.xml,内容如下:
<linker>
<assembly fullname="MoonSharp.Interpreter">
<type fullname="MoonSharp.*" preserve="all" />
</assembly>
</linker>
步骤2:引入命名空间
在代码顶部添加:
C#
using MoonSharp.Interpreter;
VB.NET
Imports MoonSharp.Interpreter
步骤3:调用脚本
以下示例演示如何用MoonSharp计算阶乘:
C#
double MoonSharpFactorial()
{
string script = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end
return fact(5)";
DynValue res = Script.RunString(script);
return res.Number;
}
VB.NET
Function MoonSharpFactorial() As Double
' VB.NET is not very strong at embedded newlines...
Dim scriptCode As String = "-- defines a factorial function" & vbCrLf &
"function fact (n)" & vbCrLf & _
"if (n == 0) then" & vbCrLf & _
"return 1" & vbCrLf & _
"else" & vbCrLf & _
"return n*fact(n - 1)" & vbCrLf & _
"end" & vbCrLf & _
"end" & vbCrLf & _
"return fact(5)" & vbCrLf
Dim res As DynValue = Script.RunString(scriptCode)
Return res.Number
End Function
步骤4:运行代码
在代码中调用MoonSharpFactorial()即可执行脚本。现在,你可以继续探索其他教程了!
2.Keeping a Script around
言语易逝,文字永存。
在之前的教程中,你首次接触了 MoonSharp:将脚本放入字符串中运行并获取输出。虽然偶尔有用,但大多数实际用例需要的互操作性需要 CLR 代码与 MoonSharp 更深度的集成。
步骤1:复现前教程所有操作
认真地说,虽然我们在这里学习的是(稍微)更高级的概念,但你最初的尝试几乎无需改动。这正是一个极好的起点。
步骤2:改为创建Script对象
首先要做的更改是创建一个脚本对象,而不是使用静态方法之一。这并非什么难事,但它为我们接下来的发展奠定了基础。
double MoonSharpFactorial()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end
return fact(5)";
Script script = new Script();
DynValue res = script.DoString(scriptCode);
return res.Number;
}
步骤3:访问全局环境
现在有了脚本对象,我们可以修改函数运行的全局环境。例如,改变阶乘函数的输入参数,让程序能指定计算目标数值的阶乘。
double MoonSharpFactorial()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end
return fact(mynumber)";
Script script = new Script();
script.Globals["mynumber"] = 7;
DynValue res = script.DoString(scriptCode);
return res.Number;
}
通过简单的 script.Globals 表引用语法,我们实现了向 MoonSharp 脚本注入数值。实际上不仅能传递数字,后续还将演示如何传递函数和对象,但现阶段我们暂限于数字、布尔值和字符串。
步骤4:直接调用函数
我们学习了如何让 MoonSharp 计算从外部选择的数字的阶乘。但是,以这种方式完成,感觉像是一个肮脏的黑客行为(尽管如此,这是一项重要的技术,我们将经常使用)。
下面是如何从C#调用Lua函数。
double MoonSharpFactorial2()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end";
Script script = new Script();
script.DoString(scriptCode);
DynValue res = script.Call(script.Globals["fact"], 4);
return res.Number;
}
我们来看看具体做了哪些调整。
首先,我们删除了脚本结尾的 return 语句——这个语句本来可以保留,但由于我们需要通过自定义参数调用 fact 函数,保留它反而会导致冗余。
此时,script.DoString(...) 的调用将会执行整个脚本文件,在全局环境中留下一个完整可用的 fact 函数。
随后我们添加了这一行代码:
DynValue res = script.Call(script.Globals["fact"], 4);
这段代码会从脚本的全局环境中获取 fact 函数,并以参数4进行调用。
虽然存在更高效的实现方式(特别是在性能方面),但如果您不需要在时间敏感的循环中进行大量调用,这无疑是最简单的解决方案(不过存在一定的类型安全隐患)。
值得注意的是,fact 函数可以任意次数被调用——因为 Script 会保留其运行状态,并随时准备好被反复执行。
3.DynValue revealed
一切皆是 DynValue,DynValue 即一切。
DynValue 概念实际上是 MoonSharp 的核心基础,尽管到目前为止我们几乎没有涉及太多,但事实上,想要深入了解而不触及这一主题是不太可能的。
如标题所述,MoonSharp 中的(几乎)所有内容都是 DynValue 对象的实例。一个 DynValue 代表脚本中的一个值,无论其类型如何,因此它可以是一个表、一个函数、一个数字、一个字符串或其他任何东西。
那么,让我们从最新的教程步骤开始,并将其改为使用 DynValue。
步骤1:重新执行你在上一个教程中的所有操作
我们再次从上一个教程的最后一步开始。你已经完成了吧?
你可以在这里获取参考文档。
步骤2:将 fact 函数存入 DynValue
第一个改动是我们以不同的方式获取 fact 变量:
double MoonSharpFactorial()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end";
Script script = new Script();
script.DoString(scriptCode);
DynValue luaFactFunction = script.Globals.Get("fact");
DynValue res = script.Call(luaFactFunction, 4);
return res.Number;
}
好的,让我们在这一行花点时间:
DynValue luaFactFunction = script.Globals.Get("fact");
这样可以从脚本的全局作用域中获取 fact 函数。与索引器属性不同,「Get」方法会直接返回一个「DynValue」类型,而索引器则会尝试将其转换为「System.Object」。在本例中,由于我们需要获取的是「DynValue」类型,因此选择使用「Get」方法。
步骤3:将数字存入 DynValue
如果我们希望避免额外的类型转换,可以直接以「DynValue」形式传递数字,而无需依赖「Call」方法提供的隐式转换。虽然当前场景中这不是必需的,但在其他情况下可能会用到,毕竟并非所有操作都会自动帮你完成类型转换!
double MoonSharpFactorial2()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n*fact(n - 1)
end
end";
Script script = new Script();
script.DoString(scriptCode);
DynValue luaFactFunction = script.Globals.Get("fact");
DynValue res = script.Call(luaFactFunction, DynValue.NewNumber(4));
return res.Number;
}
所以,我们用以下内容替换了我们的数字:
DynValue.NewNumber(4)
DynValue 有许多工厂方法,都以 "New" 开头(如 NewString、NewNumber、NewBoolean 等),可用于从头开始构建我们的值。
它还有一个方便的 FromObject 方法,可以从对象创建 DynValue,这正是 Call 在幕后使用的方法,以简化我们的工作。
步骤 4:了解 DataType(s)
在 DynValue 中,最重要的属性之一是 Type。
Type 属性是一个枚举,告诉我们 DynValue 中包含了什么类型的数据。
每当我们想知道 DynValue 中包含了什么,我们都可以查询 Type 属性:
// Create a new number
DynValue v1 = DynValue.NewNumber(1);
// and a new string
DynValue v2 = DynValue.NewString("ciao");
// and another string using the automagic converters
DynValue v3 = DynValue.FromObject(new Script(), "hello");
// This prints Number - String - String
Console.WriteLine("{0} - {1} - {2}", v1.Type, v2.Type, v3.Type);
// This prints Number - String - Some garbage number you shouldn't rely on to be 0
Console.WriteLine("{0} - {1} - {2}", v1.Number, v2.String, v3.Number);
重要的是要知道,DynValue 的某些属性只有在值是给定类型时才有意义。例如,只有当类型为 DataType.Number 时,Number 属性才能保证有意义的值,String 属性也是如此。
除非你确定 DynValue 包含什么,否则在使用其属性之前,请务必检查 DynValue 的 Type。假设 DynValue 包含一个给定类型,而它实际上包含另一个类型,这是错误和问题的常见来源。
步骤 5:元组
正如你(应该)知道的,Lua 可以从函数(以及其他情况)返回多个值。
为了处理这种情况,使用了一种特殊的 DynValue 类型(DataType.Tuple)。元组有一个 Tuple 属性,它是 DynValue 对象的数组,这些对象是元组的成员。
这比听起来更简单:
DynValue ret = Script.RunString("return true, 'ciao', 2*3");
// prints "Tuple"
Console.WriteLine("{0}", ret.Type);
// Prints:
// Boolean = true
// String = "ciao"
// Number = 6
for (int i = 0; i < ret.Tuple.Length; i++)
Console.WriteLine("{0} = {1}", ret.Tuple[i].Type, ret.Tuple[i]);
结语
关于 DynValue 的介绍就到这里。还有很多需要学习的内容,但这些应该足以让你入门了。
请记住这些内容,因为这是一切的核心。
4.Calling back C#
但也支持 F# 和 VB.NET 和 C++/CLI 和 Boo 等等...
脚本在应用程序中非常有用,因为它们可以从包含应用程序本身实现的构建块开始,自定义业务逻辑。无论您是在商业应用程序、视频游戏还是某种工具中嵌入脚本,您的首要关注点是脚本和应用程序的互操作性以及两者之间的对象共享(在某种意义上)。
而过程编程中的基本构建块是函数。
步骤 1:永远不要厌倦阶乘
让我们从到目前为止我们使用的标准阶乘脚本开始:
private static double CallbackTest()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return n * fact(n - 1);
end
end";
Script script = new Script();
script.DoString(scriptCode);
DynValue res = script.Call(script.Globals["fact"], 4);
return res.Number;
}
步骤 2:自定义乘法函数
好的,假设我们希望乘法在宿主应用程序的 API 函数中实现。在这种情况下,这样做显然没有任何目的,但我们在这里是为了学习,所以让我们假装这是一个好主意。
private static int Mul(int a, int b)
{
return a * b;
}
private static double CallbackTest()
{
string scriptCode = @"
-- defines a factorial function
function fact (n)
if (n == 0) then
return 1
else
return Mul(n, fact(n - 1));
end
end";
Script script = new Script();
script.Globals["Mul"] = (Func<int, int, int>)Mul;
script.DoString(scriptCode);
DynValue res = script.Call(script.Globals["fact"], 4);
return res.Number;
}
就是这样!
script.Globals["Mul"] = (Func<int, int, int>)Mul;
这将全局环境中的 Mul 变量设置为指向应用程序的 Mul 委托。我们在这里将委托强制转换为它的 Func<int, int, int> 类型以取悦 C# 编译器 - 无论您使用什么技术将委托强制转换为 System.Object 都可以。
另外,请注意,我们将方法定义为静态的,但在这种情况下,它们完全不需要是静态的;实例方法也可以使用。
返回一系列数字
问题:有一个API函数,它返回一个整数序列。脚本将对收到的数字求和,并返回总数。
private static IEnumerable<int> GetNumbers()
{
for (int i = 1; i <= 10; i++)
yield return i;
}
private static double EnumerableTest()
{
string scriptCode = @"
total = 0;
for i in getNumbers() do
total = total + i;
end
return total;
";
Script script = new Script();
script.Globals["getNumbers"] = (Func<IEnumerable<int>>)GetNumbers;
DynValue res = script.DoString(scriptCode);
return res.Number;
}
在这里,也没有太难的地方。你可以看到如何将一个 IEnumerable(或 IEnumerator)即时转换为 Lua 迭代器,以便脚本运行。
还要注意,脚本中直接包含了可执行代码,因此在 DoString 时刻就可以访问 getNumbers 方法,无需进行 Call.. 调用。记住这一点,DoString 和 DoFile 将立即执行脚本中包含的代码!
还有一点必须注意。MoonSharp 能够转换 System.Collections.IEnumerable 和 System.Collections.IEnumerator 类型的迭代器。也就是说,非泛型的变体。如果由于某种原因你实现了一个泛型迭代器而没有实现非泛型迭代器,那么迭代器将无法工作。所有标准的集合类型和迭代器方法,如上面的方法,默认实现了非泛型变体,所以不需要太担心。
返回表格
问题:有一个API函数,这次返回一个整数序列,存储在一个表格中。
private static List<int> GetNumberList()
{
List<int> lst = new List<int>();
for (int i = 1; i <= 10; i++)
lst.Add(i);
return lst;
}
private static double TableTest1()
{
string scriptCode = @"
total = 0;
tbl = getNumbers()
for _, i in ipairs(tbl) do
total = total + i;
end
return total;
";
Script script = new Script();
script.Globals["getNumbers"] = (Func<List<int>>)GetNumberList;
DynValue res = script.DoString(scriptCode);
return res.Number;
}
在这里,我们可以看到 List<int> 是如何自动转换为 Lua 表的!请注意,生成的表将像 Lua 表通常那样以1为索引。
然而,我们可以做得更好。我们可以直接在函数内部构建一个 Lua 表:
private static Table GetNumberTable(Script script)
{
Table tbl = new Table(script);
for (int i = 1; i <= 10; i++)
tbl[i] = i;
return tbl;
}
private static double TableTest2()
{
string scriptCode = @"
total = 0;
tbl = getNumbers()
for _, i in ipairs(tbl) do
total = total + i;
end
return total;
";
Script script = new Script();
script.Globals["getNumbers"] = (Func<Script, Table>)(GetNumberTable);
DynValue res = script.DoString(scriptCode);
return res.Number;
}
您可以看到使用 Table 对象操作表是多么容易。
但是有两点需要注意:
要创建一个新的 Table 对象,您必须有一个对正在执行的脚本的引用
如果在 CLR 函数中有一个 Script 参数可供 Lua 脚本使用,MoonSharp 将为您填充它。这也适用于(不太可能以这种方式使用的)ScriptExecutionContext 和 CallbackArguments 类型。如果您不知道这些是做什么的,不用担心,它们不是让 MoonSharp 基本工作所必需的!
作为一个好的实践,如果可以的话,总是保留 Script 对象。有些事情(比如创建表)只能使用 Script 对象来完成。
接收一个表
一个表会自动转换为 List<T>。例如:
public static double TableTestReverse()
{
string scriptCode = @"
return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
";
Script script = new Script();
script.Globals["dosum"] = (Func<List<int>, int>)(l => l.Sum());
DynValue res = script.DoString(scriptCode);
return res.Number;
}
但是,这可能会在某些平台(比如 iOS)上造成问题。有很多方法可以解决这个问题(你会在其他教程中看到),但可以说,以下方法没有任何问题,而且速度更快:
public static double TableTestReverseSafer()
{
string scriptCode = @"
return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
";
Script script = new Script();
script.Globals["dosum"] = (Func<List<object>, int>)(l => l.OfType<int>().Sum());
DynValue res = script.DoString(scriptCode);
return res.Number;
}
另一种更快的方法是使用 Table 对象:
static double Sum(Table t)
{
var nums = from v in t.Values
where v.Type == DataType.Number
select v.Number;
return nums.Sum();
}
private static double TableTestReverseWithTable()
{
string scriptCode = @"
return dosum { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
";
Script script = new Script();
script.Globals["dosum"] = (Func<Table, double>)Sum;
DynValue res = script.DoString(scriptCode);
return res.Number;
}
但在这里,我们必须处理 DynValue(s)。
要理解所有这些,我们需要更深入地了解 MoonSharp 如何将 Lua 类型映射到 C# 类型,反之亦然。我们将在下一部分中详细讨论。
5.Auto-conversions
这次没有代码。
在深入探讨 MoonSharp 和 CLR 集成之前,我们需要明确类型是如何来回映射的。遗憾的是,反向映射与正向映射有很大不同,因此我们将分别分析这两种情况。
你可能会问,这是不是有点太复杂了?
当然复杂。自动化的东西很好,但当它们失败时,失败的方式却非常复杂。当有疑问或事情变得过于复杂时,你需要简化事情。
有两种方法:要么直接使用 DynValue,要么使用自定义转换器。
这两种解决方案不仅能让你获得理智和简单,而且在速度上也明显快于自动转换!
自定义转换器
可以自定义转换过程,但设置是全局的,会影响所有脚本。
要自定义转换,只需将适当的回调设置为 Script.GlobalOptions.CustomConverters。
例如,如果我们希望在从 CLR 转换为脚本时,将所有 StringBuilder 对象转换为大写字符串,可以这样做:
Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion<StringBuilder>(
v => DynValue.NewString(v.ToString().ToUpper()));
如果我们想自定义所有表格在与 IList<int> 匹配时的转换方式,我们可以这样写:
Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(IList<int>),
v => new List<int>() { ... });
如果一个转换器返回 null,系统会表现得就像没有自定义转换器存在一样,并尝试进行自动转换。
CLR 类型到 MoonSharp 类型的自动转换
这个转换适用于以下情况:
• 从脚本调用的函数返回对象时
• 从用户数据的属性返回对象时
• 使用索引运算符在表中设置值时
• 调用 DynValue.FromObject 时
• 使用任何以 System.Object 代替 DynValue 作为参数的函数重载时
这个转换实际上非常简单。下表解释了具体的转换规则:
CLR type | C# friendly name | Lua type | Notes |
---|---|---|---|
void | (no value) | This can be applied to return values of methods only. 翻译:这只能应用于方法的返回值。 | |
null | nil | Any null will be converted to nil. 翻译:任何null都将转换为nil。 | |
MoonSharp.Interpreter.DynValue | * | The DynValue is passed through. 翻译:DynValue 被传递了。 | |
System.SByte | sbyte | number | |
System.Byte | byte | number | |
System.Int16 | short | number | |
System.UInt16 | ushort | number | |
System.Int32 | int | number | |
System.UInt32 | uint | number | |
System.Int64 | long | number | The conversion can lead to a silent precision loss. 翻译:转换可能导致精度无声地丢失。 |
System.UInt64 | ulong | number | The conversion can lead to a silent precision loss. 翻译:转换可能导致精度无声地丢失。 |
System.Single | float | number | |
System.Decimal | decimal | number | The conversion can lead to a silent precision loss. 翻译:转换可能导致精度无声地丢失。 |
System.Double | double | number | |
System.Boolean | bool | boolean | |
System.String | string | string | |
System.Text.StringBuilder | string | ||
System.Char | char | string | |
MoonSharp.Interpreter.Table | table | ||
MoonSharp.Interpreter.CallbackFunction | function | ||
System.Delegate | function | ||
System.Object | object | userdata | Only if the type has been registered for userdata. 翻译:只有当该类型已经为 userdata 注册过,才可以这样做。 |
System.Type | userdata | Only if the type has been registered for userdata, static members access. 翻译:只有当类型已经为 userdata 注册时,才可以访问静态成员。 | |
MoonSharp.Interpreter.Closure | function | ||
System.Reflection.MethodInfo | function | ||
System.Collections.IList | table | The resulting table will be indexed 1-based. All values are converted using these rules. 翻译:生成的表将使用从1开始的索引。所有的值都会按照以下规则进行转换。 | |
System.Collections.IDictionary | table | All keys and values are converted using these rules. 翻译:所有键和值都会使用这些规则进行转换。 | |
System.Collections.IEnumerable | iterator | All values are converted using these rules. 翻译:所有值均依下列规则进行转换。 | |
System.Collections.IEnumerator | iterator | All values are converted using these rules. 翻译:所有值均依下列规则进行转换。 |
这包括对集合的相当全面的覆盖,因为大多数集合都实现了 IList、IDictionary、IEnumerable 和/或 IEnumerator 接口。如果你正在编写自己的集合,请记得实现这些非泛型接口中的一个。
任何不能使用此逻辑转换的值都将抛出 ScriptRuntimeException。
MoonSharp 类型到 CLR 类型的标准自动转换
相反的转换要复杂得多。实际上,存在两条不同的转换路径--"标准"路径和"受限"路径。当你要求将一个 DynValue 转换为对象而不指定你实际想要接收什么时,应用第一种路径,而当有一个目标 System.Type 要匹配时,则应用另一种路径。
这在以下情况下使用:
- 调用 DynValue.ToObject 时
- 使用索引器从表中检索值时
- 在受限转换的一些特定子情况下(见下文)
这里我们看到默认转换。它实际上很简单:
MoonSharp type | CLR type | Notes |
---|---|---|
nil | null | Applied to every value which is nil. 翻译:应用于每个值为 nil 的情况。 |
boolean | System.Boolean | |
number | System.Double | |
string | System.String | |
function | MoonSharp.Interpreter.Closure | If DataType is Function. 翻译:如果数据类型是函数(Function)。 |
function | MoonSharp.Interpreter.CallbackFunction | If DataType is ClrFunction. 翻译:如果数据类型是 CLR 函数(ClrFunction)。 |
table | MoonSharp.Interpreter.Table | |
tuple | MoonSharp.Interpreter.DynValue[] | |
userdata | (special) | Returns the object stored in userdata. If a "static" userdata, the Type is returned. 翻译:如果是存储在 userdata 中的对象,此方法会返回该对象。如果是“静态”userdata,则返回其类型(Type)。 |
无法使用此逻辑转换的每个值都将抛出 ScriptRuntimeException 异常。
MoonSharp 类型到 CLR 类型的受限自动转换
非常感谢您提供这些关于 MoonSharp 类型转换的详细信息!我来总结一下要点:
MoonSharp 类型到 CLR 类型的受限自动转换比默认转换要复杂得多。它发生在需要将 MoonSharp 值存储到给定类型的 CLR 变量中的情况下,转换方式几乎有无限多种。
特殊情况:
- 如果目标类型是 DynValue,则不进行转换,返回原始值。
- 如果目标类型是 object,则应用默认转换。
- 如果要映射 nil 值,则 null 映射到引用类型和可空值类型。某些情况下会尝试匹配默认值。否则抛出异常。
- 如果没有提供值,则尽可能使用默认值。
数据类型转换:
- 字符串可转换为 String, StringBuilder 或 Char
- 布尔值可转换为 Boolean 及其可空版本,也可转换为字符串类型
- 数字可转换为 SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Decimal, Double 及其可空版本,也可转换为字符串类型
- 函数转换为 Closure, ScriptFunctionDelegate, ClrFunction 或 Func 委托
- 非静态的 userdata 如果类型兼容则可转换。也可通过 ToString() 转换为字符串类型
- 表可转换为 Table, Dictionary, List, 数组等多种集合类型,但转换到泛型和类型化数组有一些限制,可能会有性能和兼容性问题
总之,MoonSharp 在类型转换上非常灵活,但复杂类型的转换可能存在一些问题。必要时可以添加自定义转换器来解决。在面向 AOT 平台时,要特别小心值类型的泛型可能引入的不兼容问题。
end