如何使用 setmetatable 来创建 Lua 包装类以包裹一个 C# 对象

我正在用 C# 为我的游戏创建一些 UI,并希望将所有东西暴露给 Lua,以便我的艺术家可以进行小的调整而不需要进行任何代码。我正在使用 MoonSharp 将 Lua 脚本集成到我的项目中。

这是我当前的 UIElement 包装类的情况:

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# 类,通过 MoonSharp API 暴露给 Lua。当直接使用对象时,它可以正常工作,并具有诸如 SetPos、SetColor 等函数。

UIElement 旨在成为我的 Lua 中的“类”,以包装并扩展我的 C# 对象。

当我在脚本的其他地方实例化 UIElement 并尝试调用函数(例如 SetPos)时,它确实进入了 __index 函数。但是,rawget 调用总是返回 nil。似乎与 BLU_UIElement 无关。我已经尝试了非常简单的事情,例如在构造函数中添加字符串 ID 值,并在 __index 函数中尝试 rawget 它,但它也返回 nil。

我想我可能没有正确地设置类或对象本身的元表,但我不确定问题出在哪里。我一直在这里寻找建议:http://lua-users.org/wiki/ObjectOrientationTutorial,但是没有什么能引起我的注意。

我欢迎任何关于此的指导,我已经看了几天了,也没有找出来,搜索在线通常只显示类似于我已经做过的代码。

点赞
用户11493760
用户11493760

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

就我而言,在 MoonSharp 中,像你试图在下面这样做的那样,NativeClass.__new() 从未成功:

self.__codeElement = BLU_UIElement.__new();

出于这个原因,我为我的本地类创建了自定义构造函数,并将它们作为委托传递给全局名称空间(但它的类型必须被注册)。它看起来很像你通常构造一个对象。只是没有 new 关键字:

在 C#中:

public NativeClass{

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

}

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

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

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

x = NativeClass()

编辑:因此,我没有读到你尝试使用字符串来实现的情况。也许你应该考虑不在 LUA 中编写包装器类,而是在 C#中编写它,或者有什么原因禁止这样做?

2019-05-14 10:45:39
用户11388978
用户11388978

我有一个比我更经验丰富的使用Lua元表的朋友来看了一下。为了帮助其他人,我在这里发布答案。

问题在于我试图将UIElement表同时用作“类”表和“对象”元表。在__index函数中调用rawget时,它试图在UIElement表中查找,而不是在UIElement.new()中创建的self表中查找。将这两个拆分为不同的表(一个用于类,一个用于对象元表)可以解决问题。

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

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
2019-05-15 06:10:19