MoonSharp 文档一
目录
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 类型的受限自动转换
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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endendreturn 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)" & vbCrLfDim 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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endendreturn fact(5)";Script script = new Script();DynValue res = script.DoString(scriptCode);return res.Number;
}
步骤3:访问全局环境
现在有了脚本对象,我们可以修改函数运行的全局环境。例如,改变阶乘函数的输入参数,让程序能指定计算目标数值的阶乘。
double MoonSharpFactorial()
{string scriptCode = @" -- defines a factorial functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endendreturn 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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endend";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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endend";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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n*fact(n - 1)endend";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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn n * fact(n - 1);endend";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 functionfunction fact (n)if (n == 0) thenreturn 1elsereturn Mul(n, fact(n - 1));endend";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() dototal = total + i;endreturn 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) dototal = total + i;endreturn 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) dototal = total + i;endreturn 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.Valueswhere v.Type == DataType.Numberselect 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. | |
MoonSharp.Interpreter.DynValue | * | The DynValue is passed through. | |
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. |
System.Type | userdata | Only if the type has been registered for userdata, static members access. | |
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. | |
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. |
boolean | System.Boolean | |
number | System.Double | |
string | System.String | |
function | MoonSharp.Interpreter.Closure | If DataType is Function. |
function | MoonSharp.Interpreter.CallbackFunction | If DataType is 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. |
无法使用此逻辑转换的每个值都将抛出 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
相关文章:
MoonSharp 文档一
目录 1.Getting Started 步骤1:在 IDE 中引入 MoonSharp 步骤2:引入命名空间 步骤3:调用脚本 步骤4:运行代码 2.Keeping a Script around 步骤1:复现前教程所有操作 步骤2:改为创建Script对象 步骤…...
【linux网络编程】端口
一、端口(Port)概述 在计算机网络中,端口(Port) 是用来标识不同进程或服务的逻辑通信端点。它类似于一座大楼的房间号,帮助操作系统和网络协议区分不同的应用程序,以便正确地传输数据。 1. 端口…...
Vulnhub-Node
目录标题 一、靶机搭建二、信息收集靶机信息扫ip扫开放端口和版本服务信息指纹探测目录扫描 三、Web渗透信息收集zip爆破ssh连接 四、提权内核版本提权 利用信息收集拿到路径得到账户密码,下载备份文件,base64解密后,利用fcrackzip爆破zip压缩…...
RK3568平台(camera篇)camera3_profiles_rk3588.xml解析
camera3_profiles_rk3588.xml 是一个与 Android 相机 HAL(硬件抽象层)相关的配置文件,通常用于定义 Rockchip RK3588 平台上的相机设备及其功能。该文件基于 Android 的 Camera3 HAL 框架,用于描述相机的配置、流配置、分辨率、帧率、格式等信息。 以下是对 camera3_profi…...
高阶哈希算法
SHA-256简介 SHA-256 是 **SHA-2(Secure Hash Algorithm 2)**家族中的一种哈希算法,由美国国家安全局设计,并于 2001 年发布。它能够将任意长度的数据映射为一个固定长度256 位,即 32 字节的哈希值,通常以…...
Spark数据倾斜深度解析与实战解决方案
Spark数据倾斜深度解析与实战解决方案 一、数据倾斜的本质与影响 数据倾斜是分布式计算中因数据分布不均导致的性能瓶颈现象。当某些Key对应的数据量远超其他Key时,这些"热点Key"所在的Task会消耗80%以上的计算时间,成为整个作业的木桶短板。具体表现为: Task执…...
Kubernetes滚动更新实践
前言 在我之前的项目中,对微服务升级采用的做法是删除整个namespace, 再重新应用所有yaml。 这种方式简单粗暴,但是不可避免的导致服务中断,影响了用户体验 为了解决更新服务导致的服务中断问题, Kubernetes提供了一种…...
Broken pipe
比较常见的一个问题。 但是并不是每个人都能说清楚。 首先注意下写法: Broken pipe # B大写 p小写 主要是grep的时候别写错了 常见的原因 1、客户端关闭连接。 在服务器端处理请求的过程中,客户端突然关闭了连接,例如浏览器关闭、网络断开…...
doris:ClickHouse
Doris JDBC Catalog 支持通过标准 JDBC 接口连接 ClickHouse 数据库。本文档介绍如何配置 ClickHouse 数据库连接。 使用须知 要连接到 ClickHouse 数据库,您需要 ClickHouse 23.x 或更高版本 (低于此版本未经充分测试)。 ClickHouse 数据库的 JDBC 驱动程序&a…...
前K个高频单词
692. 前K个高频单词 - 力扣(LeetCode) 题目描述: 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。 返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序…...
恢复IDEA的Load Maven Changes按钮
写代码的时候不知道点到什么东西了,pom文件上的这个弹窗就是不出来了,重启IDEA,reset windos都没用,网上搜也没收到解决方案 然后开打开其他项目窗口时,看到那个的功能名叫 Hide This Notification 于是跑到Setting里…...
【五.LangChain技术与应用】【31.LangChain ReAct Agent:反应式智能代理的实现】
一、ReAct Agent是啥?为什么说它比「普通AI」聪明? 想象一下,你让ChatGPT查快递物流,它可能直接编个假单号糊弄你。但换成ReAct Agent,它会先推理(Reasoning)需要调用哪个接口,再行动(Action)查询真实数据——这就是ReAct的核心:让AI学会「动脑子」再动手。 举个真…...
Leetcode 62: 不同路径
Leetcode 62: 不同路径 问题描述: 一个机器人位于一个 (m \times n) 网格的左上角(起始点位于 ((0, 0)))。 机器人每次只能向下或向右移动一步。网格的右下角为终点(位于 ((m-1, n-1)))。 计算机器人从左上角到右下角…...
计算机毕业设计SpringBoot+Vue.js火锅店管理系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Docker Desktop 4.38 安装与配置全流程指南(Windows平台)
一、软件定位与特性 Docker Desktop 是容器化应用开发与部署的一体化工具,支持在本地环境创建、管理和运行Docker容器。4.38版本新增GPU加速支持、WSL 2性能优化和Kubernetes 1.28集群管理功能,适用于微服务开发、CI/CD流水线搭建等场景。 二、安装环境…...
算法系列之广度优先搜索解决妖怪和尚过河问题
在算法学习中,广度优先搜索(BFS)是一种常用的图搜索算法,适用于解决最短路径问题、状态转换问题等。本文将介绍如何利用广度优先搜索解决经典的“妖怪和尚过河问题”。 问题描述 有三个妖怪和三个和尚需要过河。他们只有一条小船…...
【技术白皮书】内功心法 | 第一部分 | IP协议的目的与工作原理(IP地址)
目录 IP协议的介绍IP协议的目的与工作原理IP协议处理过程与信件传递的相似IP协议处理过程与信件传递的区别IP协议中的概念IP数据包IP地址IP地址组成IP地址分类和组成A、B、C三类地址的格式设计特殊类型的IP地址与传统通信地址进行类比IP地址的表示五类IP地址的地址范围IP地址的…...
【Linux】外接硬盘管理
查看外接硬盘信息 连接外接硬盘后,使用以下命令识别设备: lsblk:列出块设备及其挂载点 lsblk示例输出可能显示设备名称如 /dev/sdb。 通过 lsblk -f 可同时显示文件系统类型和 UUID。 fdisk -l:列出所有磁盘的分区信息ÿ…...
【Hadoop】详解Zookeeper选主流程
1. ZooKeeper 的工作原理 Zookeeper 的核心是Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。 为了保证事务的顺序一致性,Zookeeper采用了递增的事务id号(zxid)来标识事务。所有…...
C语言-语法
数据类型 字符串 C中字符串拼接不用+号,直接使用空格。 char* str = "hello" "world"; 换行链接,加上\就不会报错 char* longStr = "00000000000000000000000000000\ 00000000000000000000000000000"; typedef C 语言提供了 typedef …...
Flink Forward Asia 2024 大会 内容整理
一、Flink 2.0:十年磨一剑,开启流计算新纪元 作为 Flink 诞生十周年的重磅更新,Flink 2.0 被定位为“面向未来十年的流计算引擎”,核心目标是解决云原生、AI 融合、存算效率等新时代挑战。以下是它的几大杀手锏: 1. …...
golang进阶知识专项-理解值传递
在 Go 语言中,所有函数的参数传递都是值传递(Pass by Value)。当你将一个变量作为参数传递给函数时,实际上传递的是该变量的副本,而不是变量本身。理解这一点对于避免常见的编程错误至关重要。根据不同的类型ÿ…...
2020年联考《申论》第三题(河北县级卷)
材料: 总面积不过八平方米的店铺,摆满货品之后,两人在店内走动都有些困难,但这家小店在当地却是小有名气的老字号。69岁的店主老林是资深木雕艺人,更被称为“最后的手工酸枝筷子传人”。手工木筷取材自缅甸坤甸木、老挝…...
Kali WebDAV 客户端工具——Cadaver 与 Davtest
1. 工具简介 在 WebDAV 服务器管理和安全测试过程中,Cadaver 和 Davtest 是两款常用的命令行工具。 Cadaver 是一个 Unix/Linux 命令行 WebDAV 客户端,主要用于远程文件管理,支持文件上传、下载、移动、复制、删除等操作。Davtest 则是一款…...
八点八数字科技:开启数字人应用的无限可能
在数字科技飞速发展的时代,八点八数字科技凭借卓越实力,成为行业的领军者。自 2014 年成立,公司汇聚近百位顶尖人才,手握 30 新型发明专利与 80 软件著作权,完成数千万融资,实力备受认可。其自主研发的全…...
Vue 使用 vue-router 时,多级嵌套路由缓存问题处理
Vue 使用 vue-router 时,多级嵌套路由缓存问题处理 对于三级菜单(或多级嵌套路由),vue 都是 通过 keep-alive 组件来实现路由组件的缓存。 有时候三级或者多级路由时,会出现失效情况。以下是三级菜单缓存的例子。 最…...
《实战AI智能体》Deepseek可以做什么?自然语言理解与分析
在人工智能技术快速迭代的今天,Deepseek凭借其先进的自然语言处理能力,正在重塑人机交互的边界。本文将从技术实现维度,深入解析该平台在自然语言理解、知识推理与文本分类三大核心领域的技术突破与应用实践。 一、深度语义理解引擎 Deepseek构建了多层级的语义解析架构,实…...
虚拟主机认证功能
一.认证功能 类型: 1.基于客户端地址的认证 2.基于用户的认证 1.基于客户端地址的认证 nginx是一款模块化软件,功能都是基于模块实现的。 如上图所示,访问认证是使用的这个access_module模块。 这个模块也给我们提供了一些指令…...
BGP协议深度解析:从背景到术语的全面梳理
引言 在复杂的网络架构中,不同自治系统(AS)之间的路由交互至关重要。BGP(Border Gateway Protocol,边界网关协议)作为自治系统间的动态路由协议,承担着跨 AS 路由传递的关键任务。本文将深入探讨…...
初步认识线程
概念 一个线程就是一个 "执行流",每一个线程之间都可以按照顺序执行自己的代码,多个线程之间可以 "同步" 执行多份代码 比如说,原本一个人做的事情,现在交给三个人一起做,那么这三个人就是线程 使用原因 那么为什么要有线程呢?我们直接使用进程不可以…...
【从零开始学习计算机科学】数字逻辑(一)绪论
【从零开始学习计算机科学】数字逻辑(一)绪论 概论信息与数字数字系统中常用的概念数字信号的描述方法概论 从数字(集成)电路的出现到计算机到网络到今天的移动互联网,数字电路是所有现代信息技术的基础。那么数字电路的基础又是什么呢?就是数字逻辑。所有数字系统都是基…...
Tomcat与Jetty的选择
Tomcat与Jetty的对比分析,分核心区别、性能表现及选型建议三部分: 一、核心区别对比 对比维度TomcatJetty架构设计多层级容器结构(Server→Service→Engine等),复杂度高基于Handler链的轻量级设计,扩展性强…...
用AI学编程2——python学习1
一个py文件,学会所有python所有语法和特性,给出注释,给出这样的文件 Python 学习整合文件 """ Python 学习整合文件 包含 Python 的基础语法、数据结构、函数定义、面向对象编程、异常处理、文件操作、高级特性等内容 每个部…...
【教程】宝塔提示请不要将网站根目录设置到以下关键目录中
【教程】宝塔提示请不要将网站根目录设置到以下关键目录中 【教程】宝塔提示请不要将网站根目录设置到以下关键目录中更换目录解决问题 先在宝塔下载一个 【教程】宝塔提示请不要将网站根目录设置到以下关键目录中更换目录解决问题_起尔网【教程】宝塔提示请不要将网站根目录设…...
html常用的文本标签以及属性
HTML标签 HTML通过一系列的标签(也成为元素),来定义文本,图像,链接等等,HTML标签是由尖括号包围的关键字。 标签通常成对出现,包括开始标签和结束标签(也成为双标签)&a…...
城市霓虹灯夜景拍照后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
调色教程 在城市霓虹灯夜景拍摄中,由于现场光线复杂等因素,照片可能无法完全呈现出当时的视觉感受。通过 Lr 调色,可以弥补拍摄时的不足。例如,运用基本调整面板中的曝光、对比度、阴影等工具,可以处理出画面的整体明暗…...
c#面试题整理
1.如何保持数据库的完整性,一致性 最好的方法:数据库约束(check,unique,主键,外键,默认,非空) 其次是:用触发器 最后:才是自己些业务逻辑,这个效率低 2.事…...
数据库基础以及基本建库建表的简单操作
文章目录 一、数据库是啥1.1、数据库的概念1.1、关系型数据库、非关系型数据库1.1、数据库服务器,数据库与表之间的关系 二、为啥要使用数据库2.1:传统数据文件存储2.2:数据库存储数据2.3、结论 三、使用数据库了会咋样四、应该咋用数据库&am…...
基于spring boot使用@Sl4j的日志功能,注解引入后爆红未生效
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 问题描述问题分析解决方案 📃文章…...
《深度剖析架构蒸馏与逻辑蒸馏:探寻知识迁移的差异化路径》
在人工智能模型优化的前沿领域,架构蒸馏与逻辑蒸馏作为知识蒸馏的关键分支,正引领着模型小型化与高效化的变革浪潮。随着深度学习模型规模与复杂度的不断攀升,如何在资源受限的情况下,实现模型性能的最大化,成为了学术…...
使用OpenCV来获取视频的帧率
在OpenCV中,获取视频的帧率(FPS, Frames Per Second)是一个常见的操作,尤其是在处理视频流或进行视频分析时。帧率表示每秒钟视频中的帧数,这个参数对于视频播放速度和时间计算非常重要。 以下是如何使用OpenCV来获取…...
计算机视觉|3D卷积网络VoxelNet:点云检测的革新力量
一、引言 在科技快速发展的背景下,3D 目标检测技术在自动驾驶和机器人领域中具有重要作用。 在自动驾驶领域,车辆需实时、准确感知周围环境中的目标物体,如行人、车辆、交通标志和障碍物等。只有精确检测这些目标的位置、姿态和类别&#x…...
vue-cli3+vue2+elementUI+avue升级到vite+vue3+elementPlus+avue总结
上一个新公司接手了一个vue-cli3vue2vue-router3.0elementUI2.15avue2.6的后台管理项目,因为vue2在2023年底已经不更新维护了,elementUI也只支持到vue2,然后总结了一下vue3的优势,最后批准升级成为了vitevue3vue-router4.5element…...
Jmeter进行http接口测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 本文主要针对http接口进行测试,使用 jmeter工具实现。 Jmeter工具设计之初是用于做性能测试的,它在实现对各种接口的调用方面已经做的比较…...
大模型AI平台DeepSeek 眼中的SQL2API平台:QuickAPI、dbapi 和 Magic API 介绍与对比
目录 1 QuickAPI 介绍 2 dbapi 介绍 3 Magic API 介绍 4 简单对比 5 总结 统一数据服务平台是一种低代码的方式,实现一般是通过SQL能直接生成数据API,同时能对产生的数据API进行全生命周期的管理,典型的SQL2API的实现模式。 以下是针对…...
靶场之路-VulnHub-DC-6 nmap提权、kali爆破、shell反连
靶场之路-VulnHub-DC-6 一、信息收集 1、扫描靶机ip 2、指纹扫描 这里扫的我有点懵,这里只有两个端口,感觉是要扫扫目录了 nmap -sS -sV 192.168.122.128 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u6 (protoc…...
nginx服务器实现上传文件功能_使用nginx-upload-module模块
目录 conf文件内容如下html文件内容如下上传文件功能展示 conf文件内容如下 #user nobody; worker_processes 1;error_log /usr/logs/error.log; #error_log /usr/logs/error.log notice; #error_log /usr/logs/error.log info;#pid /usr/logs/nginx.pid;even…...
32.C++二叉树进阶1(二叉搜索树)
⭐上篇文章:31.C多态4(静态多态,动态多态,虚函数表的存储位置)-CSDN博客 ⭐本篇代码:c学习/18.二叉树进阶-二叉搜索树 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) ⭐标⭐是比较重要的部分…...
RV1126+FFMPEG多路码流监控项目
一.项目介绍: 本项目采用的是易百纳RV1126开发板和CMOS摄像头,使用的推流框架是FFMPEG开源项目。这个项目的工作流程如下(如上图):通过采集摄像头的VI模块,再通过硬件编码VENC模块进行H264/H265的编码压缩,并把压缩后的…...
doris: SQL Server
Doris JDBC Catalog 支持通过标准 JDBC 接口连接 SQL Server 数据库。本文档介绍如何配置 SQL Server 数据库连接。 使用须知 要连接到 SQL Server 数据库,您需要 SQL Server 2012 或更高版本,或 Azure SQL 数据库。 SQL Server 数据库的 JDBC 驱动…...