目录
13.Coroutines(协程)
Lua中的协程
从CLR代码中的协程
从CLR代码中的协程作为CLR迭代器
注意事项
抢占式协程
14.Hardwire descriptors(硬编码描述符)
为什么需要“硬编码”
什么是“硬编码”
如何进行硬编码
硬编码的优缺点
硬编码是实现IL2CPP、AOT或iOS兼容性的必要条件吗?
术语表
15.Sandboxing(沙盒)
为什么需要沙盒化
沙盒化检查清单
移除“危险”的 API
16.Tips and tricks for Unity3D(Unity3D的提示和技巧)
支持的平台
其他建议
使用更显式的构造函数之一初始化脚本加载器
17.FAQ / Recipes
如何重定向打印函数的输出?
如何将输入重定向到Lua程序?
如何重定向Lua程序的IO流?
如何限制脚本在不丢失状态的情况下执行的指令数?
MoonSharp 文档一-CSDN博客
MoonSharp 文档二-CSDN博客
MoonSharp 文档三-CSDN博客
MoonSharp 文档四-CSDN博客
13.Coroutines(协程)
来自 C# 和 Lua。
文档地址:MoonSharp
Lua中的协程
Lua 中的协程是开箱即用的支持。实际上,只要你不故意排除协程模块(参见沙盒化),它们就可以免费使用。有很多注意事项(这些也偶然适用于原始的 Lua 实现),并在下面的 “注意事项” 部分进行了讨论。
使用任何 Lua 协程教程来操作它们。
从CLR代码中的协程
协程可以通过脚本 CreateCoroutine 方法创建,该方法接受一个必须是函数的 DynValue。
string code = @"
return function()
local x = 0
while true do
x = x + 1
coroutine.yield(x)
end
end
";
// Load the code and get the returned function
Script script = new Script();
DynValue function = script.DoString(code);
// Create the coroutine in C#
DynValue coroutine = script.CreateCoroutine(function);
// Resume the coroutine forever and ever..
while (true)
{
DynValue x = coroutine.Coroutine.Resume();
Console.WriteLine("{0}", x);
}
从CLR代码中的协程作为CLR迭代器
可以像调用迭代器一样调用协程:
string code = @"
return function()
local x = 0
while true do
x = x + 1
coroutine.yield(x)
if (x > 5) then
return 7
end
end
end
";
// Load the code and get the returned function
Script script = new Script();
DynValue function = script.DoString(code);
// Create the coroutine in C#
DynValue coroutine = script.CreateCoroutine(function);
// Loop the coroutine
string ret = "";
foreach (DynValue x in coroutine.Coroutine.AsTypedEnumerable())
{
ret = ret + x.ToString();
}
Assert.AreEqual("1234567", ret);
注意事项
现在我们来到了协程相关内容中最重要的部分。就像在原始的 Lua 中一样,无法从嵌套调用中执行 yield 操作。
特别是在 MoonSharp 中,如果你在从 Lua 调用的 C# 函数中调用一个脚本,你不能使用 yield 来恢复到 C# 调用外部的协程。
不过有一个解决办法:返回一个 TailCallRequest
类型的 DynValue
。
return DynValue.NewTailCallReq(luafunction, arg1, arg2...);
还可以指定一个 continuation(延续)——这是一段函数,它将在尾调用执行完成后被调用。
在 99% 的情况下,这可能是过度设计——甚至在大多数情况下,Lua 标准库也无法正确处理回调与 yield 的结合。但如果你计划自己实现像 `load`、`pcall` 或 `coroutine.resume` 这样的 API,这就是必需的。
另外,在某些边缘情况下,MoonSharp 处理 yield 的方式与标准 Lua 不同(在我尝试过的所有情况中,MoonSharp 的方式都更好,但谁知道呢)。例如,`tostring()` 支持在调用 `__tostring` 元方法时执行 yield 操作,而不会引发错误。
抢占式协程
在 MoonSharp 中,即使协程没有调用 `coroutine.yield`,也可以将其挂起。例如,如果想要非破坏性地限制脚本占用的 CPU 时间,这可能会很有用。但为了保持脚本的一致性,有一些需要注意的地方。
让我们从一个例子开始:
string code = @"
function fib(n)
if (n == 0 or n == 1) then
return 1;
else
return fib(n - 1) + fib(n - 2);
end
end
";
// Load the code and get the returned function
Script script = new Script(CoreModules.None);
script.DoString(code);
// get the function
DynValue function = script.Globals.Get("fib");
// Create the coroutine in C#
DynValue coroutine = script.CreateCoroutine(function);
// Set the automatic yield counter every 10 instructions.
// 10 is likely too small! Use a much bigger value in your code to avoid interrupting too often!
coroutine.Coroutine.AutoYieldCounter = 10;
int cycles = 0;
DynValue result = null;
// Cycle until we get that the coroutine has returned something useful and not an automatic yield..
for (result = coroutine.Coroutine.Resume(8);
result.Type == DataType.YieldRequest;
result = coroutine.Coroutine.Resume())
{
cycles += 1;
}
// Check the values of the operation
Assert.AreEqual(DataType.Number, result.Type);
Assert.AreEqual(34, result.Number);
步骤如下:
1. 创建一个协程
2. 将 `AutoYieldCounter` 设置为一个大于 0 的数字。1000 是一个不错的起点,可以根据需要调整。这个数字表示在执行多少条指令后,协程会主动挂起并返回给调用者。
3. 调用 `coroutine.Coroutine.Resume(...)` 并传入适当的参数。
4. 如果上述调用的返回类型是 `DataType.YieldRequest`,则不带参数调用 `coroutine.Coroutine.Resume()`。
5. 重复上一步,直到返回一个真正的结果类型。
6. 完成。
注意事项:
1. 如果代码重新进入(例如,`string.gsub` 的回调函数&#