如何使用 setmetatable 在 C# 对象周围创建 Lua 包装器 class

How to use setmetatable to create a Lua wrapper class around a C# object

我正在为我目前正在使用 C# 开发的游戏创建一些 UI,并希望将所有内容公开到 Lua,以便我的美工人员可以进行小的调整,而无需在代码中做任何事情。我正在使用 MoonSharp 将 Lua 脚本集成到我的项目中。

这是我目前的 UIElement 包装器 class:

UIElement = {};
UIElement.__index = UIElement;

setmetatable( UIElement, {
 __index = function( self, key )
  local codeElement = rawget( self, "__codeElement" );
  local field = codeElement and codeElement[key];
  if type( field ) == "function" then
   return function( obj, ... )
    if obj == self then
     return field( codeElement, ... );
    else
     return field( obj, ... )
    end
   end;
  else
   return field;
  end
 end,
 __call = function( cls, ... )
  return cls.new( ... );
 end,
} );

function UIElement.new()
 local self = setmetatable( {}, UIElement );
 self.__codeElement = BLU_UIElement.__new();
 return self;
end

BLU_UIElement 是我的 C# class,它通过 MoonSharp API 暴露给 Lua。直接操作对象时可以正常工作,具有SetPos、SetColor等功能

UIElement 旨在成为我在 Lua 中的 "class" 以包装和扩展我的 C# 对象。

当我在脚本的其他地方实例化一个 UIElement 并尝试调用一个函数(例如 SetPos)时,它确实正确地进入了 __index 函数。但是,rawget 调用总是 returns nil。它似乎也不是特定于 BLU_UIElement 的。我已经尝试了一些非常简单的事情,比如在构造函数中添加一个字符串 ID 值并尝试在 __index 函数中对其进行 rawget,但它也是 returns nil.

我假设我只是在 class 或对象本身上错误地设置了亚稳态,但我不确定问题出在哪里。我一直在此处查看:http://lua-users.org/wiki/ObjectOrientationTutorial 想知道我做错了什么,但我什么也没想到。

我很感激任何关于这方面的指导,我已经研究了几天都没有搞清楚,在线搜索通常只会显示与我已经在做的类似的代码。

我不得不承认我不完全确定,你试图通过在 LUA 而不是 C# 中编写你的包装器 class 来实现什么,然后公开该类型,但我注意到了这一点:

对我来说 NativeClass.__new() 从来没有像您在

尝试那样在 MoonSharp 中成功
self.__codeElement = BLU_UIElement.__new();

出于这个原因,我为我的原生 classes 创建了自定义 constructor-functions,并将它们作为委托传递给全局命名空间(尽管它的类型必须注册)。它看起来很像您通常会构造一个对象。只是没有新关键字:

在 C# 中

public NativeClass{

   public static NativeClass construct()
   {
      return new NativeClass();
   }

}

将静态方法作为委托传递给脚本:

script["NativeClass"] = (Func<NativeClass>)NativeClass.construct;

然后您可以在 MoonSharp 中像这样创建一个新实例:

x = NativeClass()

编辑:所以没有读到您曾尝试使用字符串执行此操作。也许您应该考虑不在 LUA 中编写包装器 class,而是在 C# 中,或者是否有禁止这样做的原因?

我有一个朋友在 Lua metatables 方面比我更有经验。在这里发布答案以防对其他人有帮助。

问题是我试图将 UIElement table 用作 "class" table 以及 "object" 元 table。在 __index 函数内调用 rawget 时,它试图在 UIElement table 中查找内容,而不是在 UIElement.new() 中创建的自身 table。将这两个拆分成不同的 tables(一个用于 class,一个用于对象元 table)固定的东西。

这是我更新后的工作代码:

UIElement = {};
setmetatable( UIElement, {
    __call = function( cls, ... )
        return cls.new( ... );
    end,
} );

UIElement.objectMetaTable = {
    __index = function( self, key )
        local objectValue = rawget(self, key);
        if objectValue ~= nil then
            return objectValue;
        end

        local classValue = UIElement[key];
        if classValue ~= nil then
            return classValue;
        end

        local codeElement = rawget(self, "__codeElement");
        if codeElement then
            return codeElement[key];
        end
    end,
};

function UIElement.new()
    local newInstance = setmetatable( { id = "blah" }, UIElement.objectMetaTable );
    newInstance.__codeElement = BLU_UIElement.__new();
    return newInstance;
end