当前位置: 首页 > news >正文

u3d插件xLua[四]Lua访问C#源码分析,反射,生成代码

本文将致力于研究xLua中Lua访问C#源码分析,反射,生成代码的用法

要理解xLua的源码,其实挺难的,需要的必备知识比较多:C#、Lua、反射、LuaC API

一.前言

从官网faq中的信息可以得出lua和C#的交互技术有生成代码和反射两种方式,官方没有对此作详细的解释。本文致力于从源码上分析梳理展开这部分技术。

参考:faq.md

参考:xlua源码分析(六) C#与lua的交互总结

回顾u3d插件xLua[一]环境搭建和Tutorial分析

来看这个demo,C#侧的类DerivedClass可以在Lua中当成table来使用:CS.Tutorial.DerivedClass。这个实现不是理所当然的,有大量代码在对其进行实现,下文将探索幕后的内容。

二.Demo

来看一个Demo,所有脚本改动的内容都有对比,见下图。

Demo展示的内容是:通过DoString执行Lua代码时,lua中访问C#时(比如下面的local a,local b local c),是执行了C#代码的

local a = CS.UnityEngine.Vector3 local b = CS.UnityEngine.Vector3.up local c = CS.UnityEngine.Time.deltaTime

打开场景U3DScripting

LuaTestScript.lua.txt

LuaBehaviour.cs

LuaEnv.cs

ObjectTranslator.cs

运行打印:

在Console中的打印可以看出,在lua中首次访问类时(比如Vector3),会先走LuaEnv中的元方法

function metatable:__index(key) print('***###', key) local fqn = rawget(self,'.fqn') fqn = ((fqn and fqn .. '.') or '') .. key

然后从这行进入C#

local obj = import_type(fqn)

一直执行到ObjectTranslator.TryDelayWrapLoader,这个函数是本文的核心

public bool TryDelayWrapLoader(RealStatePtr L, Type type)

阅读这个方法,可以得出其通过3种方式将C#类本身和类的成员注册到Lua,后文将做进一步分析(知道大致逻辑即可,无需精细研究)

2.1.走CodeEmit类

if (!DelegateBridge.Gen_Flag && !type.IsEnum() && !typeof(Delegate).IsAssignableFrom(type) && Utils.IsPublic(type)) { UnityEngine.Debug.Log("TryDelayWrapLoader: " + type + " -> CodeEmit"); Type wrap = ce.EmitTypeWrap(type); MethodInfo method = wrap.GetMethod("__Register", BindingFlags.Static | BindingFlags.Public); method.Invoke(null, new object[] { L }); }

2.2.走Utils.ReflectionWrap(反射)

UnityEngine.Debug.Log("TryDelayWrapLoader: " + type + " -> ReflectionWrap"); Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));

2.3.走生成代码

if (delayWrap.TryGetValue(type, out loader)) { // 有生成代码:走 XxxWrap.__Register UnityEngine.Debug.Log("TryDelayWrapLoader: " + type + " -> GenWrap.__Register"); delayWrap.Remove(type); loader(L); }

三.Lua访问C#方式一:走CodeEmit类

if (!DelegateBridge.Gen_Flag && !type.IsEnum() && !typeof(Delegate).IsAssignableFrom(type) && Utils.IsPublic(type)) { Type wrap = ce.EmitTypeWrap(type); MethodInfo method = wrap.GetMethod("__Register", BindingFlags.Static | BindingFlags.Public); method.Invoke(null, new object[] { L }); }

把这3行代码做的事放一起说:在内存中生成*Wrap类,然后将类中的成员挂到Lua的表/元表上,从而支持Lua → C# 的调用。

实现上主要在CodeEmit.EmitTypeWrap中,其比较难于理解需要较多System.Reflection.Emit中API的掌握,不然看不懂。这里先不展开,等用到了再深入研究。

public Type EmitTypeWrap(Type toBeWrap)

参考:生成动态方法和程序集

四.Lua访问C#方式二:走 Utils.ReflectionWrap(反射)

Utils.ReflectionWrap(L, type, privateAccessibleFlags.Contains(type));
public static void ReflectionWrap(RealStatePtr L, Type type, bool privateAccessible)
static void makeReflectionWrap(RealStatePtr L, Type type, int cls_field, int cls_getter, int cls_setter, int obj_field, int obj_getter, int obj_setter, int obj_meta, out LuaCSFunction item_getter, out LuaCSFunction item_setter, BindingFlags access)

ReflectionWrap中通过反射将type的成员挂到lua侧类名这个表中,核心实现在makeReflectionWrap中。通过调用LuaAPI.lua_rawset会执行类似t[k] = v的操作,然后将键和值都从栈中弹出,lua_rawset对栈顶附近的数据布局有严格的要求:

  1. 表 (Table):位于栈上的有效索引index处。

  2. 键 (Key):位于栈顶下方(-2位置)。

  3. 值 (Value):位于栈顶(-1位置)。

五.Lua访问C#方式三:走生成代码

5.1.收集

通过GetGenConfig,扫一遍当前域里几乎所有类型,把配置合并进几个静态列表

在MergeCfg中会收集特性:LuaCallCSharpAttribute

5.2.生成

由Generator.GenOne完成写入文件,由genCode保存文件文本内容,下图是为Vector2类生成UnityEngine_Vector2Wrap.cs的过程

5.3.运行

第一次Lua访问C#类中成员时,由Wrap文件调用RegisterFunc注册,来看一个例子:

LuaTestScript.lua.txt

function start() print("lua start...") local d = CS.XLuaTest.NoGc.a1 end

从调用堆栈可以看出调用链为:LuaBehaviour.Start->LuaTestScript.Start->...->XLuaTestNoGcWrap.__Register->Utils.RegisterFunc进行注册

http://www.jsqmd.com/news/1380261/

相关文章:

  • Windows 10 下 AirSim + Unreal Engine 4.27.2 环境搭建全攻略与避坑指南
  • Vue组件通信:从$emit到状态管理的实战指南
  • 二、高级C语言
  • 重庆艺术培训行业迎来“大洗牌”?从一家20年老店的坚守,看教育机构的长期主义 - 甄选测评官
  • 网站建设电话销售开场白如何破冰:让冷启动变热成交的实战指南
  • 上海有哪些高端全屋定制品牌值得看?2026全案落地实力榜 - 生活动态圈
  • langchain1.X学习笔记-18-智能体高级用法之详解ToolStrategy策略的tool_message_content参数和handle_errors参数
  • Python+Django构建物资管理系统的毕业设计实践
  • SSRF 入门指南
  • 链路采样+无损统计,低成本实现应用监控告警精准统计
  • Fable 5项目:将《红色警戒2》从x86桌面移植到iOS ARM平台的技术实践
  • Godot游戏开发:基于Excel与动态依赖注入的配置管理方案
  • 团队协作中的Git分支管理策略与实践
  • 低脂鱼丸哪家性价比高:【深鲜季】好物优选 - 18002239949
  • 告别Postman Blues:构建可靠异步消息系统的核心原理与Spring Boot+RabbitMQ实战
  • UNIX V6++进程管理:从原理到实践
  • RedHat系Linux镜像源配置全攻略:从原理到实战,解决yum/dnf慢与内网部署
  • 英文论文AI率偏高被Turnitin标红改回安全区的方法
  • 09-基于功能分支的工作流
  • 【读书笔记】《天可汗:李世民》
  • 汇川PLC编程效率提升利器:InoProShop增量粘贴功能详解与实战
  • 被 Excel 泄露风险吓过一次后,我换了个本地处理思路
  • 合页五金细分痛点解析:帝昂技术路径观察 - 生活动态圈
  • 是不是就是看什么
  • WPF MVVM模式下关闭窗体的四种实现方案与最佳实践
  • 国产牛肉供应商怎么选?从5大维度拆解一家“正规大型”供应链公司的硬指标 - 滚动商讯
  • UNODC最新犯罪报告:AI与虚拟资产正重塑东南亚犯罪生态
  • 万字深度|2026对讲机(专网通信)全产业链复盘:模数更替、公专融合、AI物联重构产业新格局
  • 5分钟掌握AI视频生成:MoneyPrinterTurbo完整技术解析
  • AI Agent记忆系统构建:从对话持久化到认知架构设计