jynew 中 xLua 配置全指南:Lua 与 C 互操作的代码生成白名单详解
2026/9/16 16:50:23 网站建设 项目流程

jynew 中 xLua 配置全指南:Lua 与 C# 互操作的代码生成白名单详解

【免费下载链接】jynewJinYongLegend-like RPG Game Framework with full Modding support and 10+ hours playable samples of game.项目地址: https://gitcode.com/GitHub_Trending/jy/jynew

导读

xLua 通过"白名单 + 代码生成"机制让 Lua 高效访问 C# 类型,其全部配置都围绕"告诉生成器哪些类型需要适配代码"展开。本文以《金庸群侠传3D重制版》jynew 仓库自带的 xLua 配置文档 为骨架,结合 GenAttributes.cs 中的 attribute 定义、ExampleGenConfig.cs 与 ExampleConfig.cs 中的实战示例,系统讲解打标签、静态列表、动态列表三种配置方式,以及LuaCallCSharpCSharpCallLuaGCOptimizeBlackList等核心标签的语义与适用场景。读完本文,你将能够为任意 C# 类型/成员正确配置 xLua 代码生成,规避 il2cpp 裁剪与反射性能陷阱,并复现 jynew 项目中的配置实践。


一、配置总览:三种方式与两条"必须"

xLua 的所有配置(即代码生成白名单)都支持以下三种声明方式:

  1. 打标签(Attribute):直接在类型或成员上标注[LuaCallCSharp][GCOptimize]等特性;
  2. 静态列表:在一个静态类中声明一个打了标签的static字段,字段类型只要实现了IEnumerable<Type>即可(BlackListAdditionalProperties两个例外有专门的类型要求,下文详述);
  3. 动态列表:在一个静态类中声明一个打了标签的static属性,Getter 是代码,可在运行时按名字空间、按程序集等条件动态筛选类型。

无论采用哪种方式,配置都受"两必须两建议"约束:

  • 列表方式必须为static字段/属性
  • 列表方式必须放在一个static类中
  • 建议不用标签方式(标签在 il2cpp 下会增加不少代码量);
  • 建议列表配置放在 Editor 目录(如果是 Hotfix 配置,且类位于 Assembly-CSharp.dll 之外的其它 dll,则必须放 Editor 目录)。

从源码看,这些约束与 GenAttributes.cs 中 attribute 的定义一一对应:LuaCallCSharpAttributeCSharpCallLuaAttributeBlackListAttributeGCOptimizeAttributeReflectionUseAttributeDoNotGenAttributeAdditionalPropertiesAttributeHotfixAttribute均通过特性标注,由编辑器侧的代码生成器扫描装配。


二、打标签(Attribute)方式

直接在 C# 类型上标注[LuaCallCSharp],xLua 即会为该类型生成适配代码。例如文档给出的最小示例:

[LuaCallCSharp] public class A { }

该方式使用方便,但应避免作为主力配置。原因是 il2cpp 下每个标签都会让生成器为该类型产出固定适配代码,从而"增加不少代码量"。jynew 仓库中 NoGc.cs 示例即展示了标签方式与GCOptimize的配合:

[GCOptimize] [LuaCallCSharp] public struct MyStruct { public MyStruct(int p1, int p2) { a = p1; b = p2; c = p2; e.c = (byte)p1; } public int a; public int b; public decimal c; public Pedding e; }

三、静态列表方式

当无法直接给类型打标签时——例如系统 API、无源码的第三方库、实例化的泛型类型——应在静态类中声明一个实现IEnumerable<Type>的静态字段并打上标签:

[LuaCallCSharp] public static List<Type> mymodule_lua_call_cs_list = new List<Type>() { typeof(GameObject), typeof(Dictionary<string, int>), };

字段必须放在静态类中,建议放在 Editor 目录。仓库中 ExampleGenConfig.cs 给出了一个完整的 LuaCallCSharp 静态列表,涵盖 Unity 常用类型与标准库:

public static class ExampleGenConfig { [LuaCallCSharp] public static List<Type> LuaCallCSharp = new List<Type>() { typeof(System.Object), typeof(UnityEngine.Object), typeof(Vector2), typeof(Vector3), typeof(Vector4), typeof(Quaternion), typeof(Color), typeof(Ray), typeof(Bounds), typeof(Ray2D), typeof(Time), typeof(GameObject), typeof(Component), typeof(Behaviour), typeof(Transform), typeof(Resources), typeof(TextAsset), typeof(Keyframe), typeof(AnimationCurve), typeof(AnimationClip), typeof(MonoBehaviour), typeof(ParticleSystem), typeof(SkinnedMeshRenderer), typeof(Renderer), typeof(WWW), typeof(Light), typeof(Mathf), typeof(System.Collections.Generic.List<int>), typeof(Action<string>), typeof(UnityEngine.Debug) }; // ... }

四、动态列表方式

声明一个静态属性并打上标签,Getter 中可编写任意筛选逻辑。文档给出的 Hotfix 示例按名字空间过滤整个程序集:

[Hotfix] public static List<Type> by_property { get { return (from type in Assembly.Load("Assembly-CSharp").GetTypes() where type.Namespace == "XXXX" select type).ToList(); } }

该属性同样必须放在静态类中,建议放在 Editor 目录。Getter 是代码,因此可以实现"按名字空间配置、按程序集配置"等任意效果。仓库 ExampleConfig.cs 中给出了一套"纯 Lua 编程"的自动化配置参考——把UnityEngineUnityEngine.UI等整个命名空间的所有导出类型(排除 delegate、interface、enum 及exclude列表中的类型)全部注入白名单:

[LuaCallCSharp] public static IEnumerable<Type> LuaCallCSharp { get { List<string> namespaces = new List<string>() { "UnityEngine", "UnityEngine.UI" }; var unityTypes = (from assembly in AppDomain.CurrentDomain.GetAssemblies() where !(assembly.ManifestModule is System.Reflection.Emit.ModuleBuilder) from type in assembly.GetExportedTypes() where type.Namespace != null && namespaces.Contains(type.Namespace) && !isExcluded(type) && type.BaseType != typeof(MulticastDelegate) && !type.IsInterface && !type.IsEnum select type); // 再拼接 Assembly-CSharp 自定义类型... return unityTypes.Concat(customTypes); } }

同文件还提供了"自动把 LuaCallCSharp 涉及到的 delegate 追加到 CSharpCallLua"(L101-L136)与"热补丁全程序集注入"(L140-L149)两份自动化模板,适合全 Lua 编程或大面积热更场景。


五、核心配置标签逐一解析

以下每个标签在 GenAttributes.cs 中都有对应 attribute 定义,语义以源码注释与文档为准。

5.1 XLua.LuaCallCSharp —— 生成 Lua 调用 C# 的适配代码

一个 C# 类型加上该配置,xLua 会生成该类型的适配代码,覆盖:构造该类型实例、访问其成员属性/方法、静态属性/方法。未配置的类型将退化为"性能较低的反射方式"访问。

关键规则:

  • 扩展方法(Extension Methods)加该配置后,适配代码会追加到被扩展类型的成员方法上;
  • xLua只生成加了该配置的类型,不会自动生成其父类的适配代码。访问子类对象的父类方法时:若父类也加了LuaCallCSharp,执行父类适配代码;否则走反射;
  • 反射访问除性能不佳外,在 il2cpp 下还可能因代码剪裁而无法访问,可用下述ReflectionUse规避。

5.2 XLua.ReflectionUse —— 阻止 il2cpp 代码剪裁

一个 C# 类型加该配置后,xLua 会生成link.xml阻止 il2cpp 对它的代码剪裁。

要点:

  • 扩展方法,必须加LuaCallCSharpReflectionUse才能被 Lua 访问到;
  • 官方建议:所有要在 Lua 访问的类型,要么加LuaCallCSharp,要么加ReflectionUse,这样才能保证各平台(尤其 il2cpp)正常运行。

5.3 XLua.DoNotGen —— 部分成员不生成代码

指明某个类中的部分函数、字段、属性不生成代码,改为反射访问。仅支持Dictionary<Type, List<string>>类型的字段或属性:key 是生效的类,value 是"不生成代码的成员名列表"。

ReflectionUse的区别:

  • ReflectionUse指明的是整个类
  • 第一次访问某成员时,ReflectionUse会把整个类都 wrap,而DoNotGen只 wrap 该成员——DoNotGen 更 lazy

BlackList的区别:

  • BlackList配置了就不能用
  • BlackList能指明某个重载DoNotGen不能。

5.4 XLua.CSharpCallLua —— 生成 C# 调用 Lua 的适配代码

若要把lua 函数适配为 C# delegate(典型场景:C# 侧各种回调、UI 事件、delegate 参数如List<T>.ForEach,或通过LuaTable.Get将 lua 函数绑定到 delegate),或把lua table 适配为 C# interface,则对应 delegate/interface 需要加该配置。

jynew 项目在 Jyx2LuaToCsBridge.cs 中正是用[CSharpCallLua]标注LBattleConfig接口来解读 Lua 侧的战斗配置表:

/// <summary>用来解读Lua的Battle配置表</summary> [CSharpCallLua] public interface LBattleConfig { int Id { get; set; } string Name { get; set; } string MapScene { get; set; } //地图 int Exp { get; set; } //获得经验 int Music { get; set; } //音乐 List<int> TeamMates { get; set; } //队友 List<int> AutoTeamMates { get; set; } List<int> Enemies { get; set; } //敌人 List<RoleInstance> DynamicTeammate { get; set; } List<RoleInstance> DynamicEnemies { get; set; } }

同文件的CsBattleConfig : LBattleConfig则作为该接口的 C# 侧实现,用于在 C# 侧生成配置对象——这正是"lua table 适配 C# interface"的典型落地。CSharpCallLua的静态列表示例见 ExampleGenConfig.cs:

[CSharpCallLua] public static List<Type> CSharpCallLua = new List<Type>() { typeof(Action), typeof(Func<double, double, double>), typeof(Action<string>), typeof(Action<double>), typeof(UnityEngine.Events.UnityAction), typeof(System.Collections.IEnumerator) };

5.5 XLua.GCOptimize —— 值类型免 GC 优化

C# 纯值类型(只包含值类型的 struct,可嵌套其它只包含值类型的 struct)或C# 枚举加该配置后,xLua 会为其生成 gc 优化代码,效果是:该值类型在 Lua 与 C# 间传递不产生 C# gc alloc,其数组访问也不产生 gc。各种无 GC 场景可参考 05_NoGc 示例。

除枚举外,包含无参构造函数的复杂类型都会生成"lua table ↔ 该类型"及其一维数组的转换代码,优化转换性能(更少 gc alloc)。

jynew 仓库中,xLua 对 UnityEngine 内置值类型的 GCOptimize 配置可直接在 GenAttributes.cs 的SysGenConfig类中看到,它采用的就是动态列表 + 属性形式:

public static class SysGenConfig { [GCOptimize] static List<Type> GCOptimize { get { return new List<Type>() { typeof(UnityEngine.Vector2), typeof(UnityEngine.Vector3), typeof(UnityEngine.Vector4), typeof(UnityEngine.Color), typeof(UnityEngine.Quaternion), typeof(UnityEngine.Ray), typeof(UnityEngine.Bounds), typeof(UnityEngine.Ray2D), }; } } // ... }

NoGc.cs 还示范了GCOptimizeCSharpCallLua的组合:自定义 structMyStruct、枚举MyEnumdecimal等均通过 delegate 参数在Update中高频传递,实现零分配。

5.6 XLua.AdditionalProperties —— GCOptimize 的扩展

这是GCOptimize的扩展配置。某些 struct 习惯把 field 做成私有、通过 property 访问,此时需要该配置——因为默认情况下GCOptimize只对 public 的 field 打解包

配置类型要求为Dictionary<Type, List<string>>:key 是生效的类型,value 是属性名列表。仓库 GenAttributes.cs 中对 UnityEngine 值类型的配置即为此模式的官方范本:

[AdditionalProperties] static Dictionary<Type, List<string>> AdditionalProperties { get { return new Dictionary<Type, List<string>>() { { typeof(UnityEngine.Ray), new List<string>() { "origin", "direction" } }, { typeof(UnityEngine.Ray2D), new List<string>() { "origin", "direction" } }, { typeof(UnityEngine.Bounds), new List<string>() { "center", "extents" } }, }; } }

5.7 XLua.BlackList —— 黑名单

如果不需要生成某个类型某些成员的适配代码,可用BlackList实现。标签方式最简单——直接在对应成员上加[BlackList]即可。

由于需要考虑"把重载函数中的某一个重载列入黑名单",配置方式的类型较复杂,为List<List<string>>

  • 第一层 List 的每个条目对应一个成员;
  • 第二层 List 是 string 列表:第一个 string 是类型的全路径名,第二个 string 是成员名;如果成员是方法,还需从第三个 string 开始,把其参数的类型全路径全部列出

文档给出的经典示例(把GameObject.networkView属性与FileInfo.GetAccessControl方法列入黑名单):

[BlackList] public static List<List<string>> BlackList = new List<List<string>>() { new List<string>(){"UnityEngine.GameObject", "networkView"}, //new List<string>(){ typeof(UnityEngine.GameObject).FullName, "networkView"}, new List<string>(){"System.IO.FileInfo", "GetAccessControl", "System.Security.AccessControl.AccessControlSections"}, //new List<string>(){ typeof(System.IO.FileInfo).FullName, "GetAccessControl", typeof(System.Security.AccessControl.AccessControlSections).FullName }, };

注释中给出了"用typeof(...).FullName替代手写字符串"的等价写法。仓库 ExampleConfig.cs 保留了完整的企业级黑名单(涉及 XmlNodeList、WWW、Texture2D、Security、Light、FileInfo、DirectoryInfo、MonoBehaviour 等),并额外提供了基于Func<MemberInfo, bool>MethodFilter动态过滤器(L277-L305),可对泛型Dictionary<,>的构造器与方法(如TryAdd、两参Remove)进行更精细的排除——这是标签方式无法表达的场景。


六、生成期配置:GenPath 与 GenCodeMenu

以下配置属于生成期配置,必须放到 Editor 目录下

配置类型说明
CSObjectWrapEditor.GenPathstring配置生成代码的放置路径,默认放在Assets/XLua/Gen/
CSObjectWrapEditor.GenCodeMenu无参数函数 + 标签用于生成引擎的二次开发,执行XLua/Generate Code菜单时会触发该函数的调用

GenPath用于自定义适配代码的输出目录;GenCodeMenu则提供钩子,让开发者可以在 Unity 菜单栏执行XLua/Generate Code生成代码的同时,自动触发自己的扩展逻辑(例如生成后自动编译、拷贝产物等)。


七、配置策略建议与实战要点

结合本文配置与 jynew 仓库实践,总结如下落地要点:

  1. 白名单优先、标签兜底:日常配置以 Editor 目录下的静态列表/动态列表为主,标签仅用于少量确定类型,避免 il2cpp 下代码量膨胀。
  2. 平台安全双保险:凡 Lua 需要访问的类型,要么LuaCallCSharp(生成适配、性能最优),要么ReflectionUse(防 il2cpp 裁剪、反射兜底),否则多平台发布存在运行时不可访问风险。
  3. 值类型优先 GCOptimize:战斗、UI 高频传递的Vector2/3/4ColorQuaternion及自定义纯值类型 struct,配合AdditionalProperties处理私有字段,可显著减少 GC 分配。
  4. delegate/interface 记得 CSharpCallLua:C# 事件回调、List<T>.ForEach、LuaTable 绑定 delegate,以及 jynew 中LBattleConfig这类"Lua 配置表 → C# interface"的数据桥接,都必须显式配置。
  5. 用 BlackList 处理平台差异:不同平台(如 WebGL)不可用或不需要的成员(参考 ExampleConfig.cs 中的#if UNITY_WEBGL分支),应通过黑名单或MethodFilter排除,避免生成无法编译或运行时出错的代码。

相关文件索引

  • 配置总文档:jyx2/Assets/XLua/Doc/configure.md
  • 所有配置 Attribute 的源码定义与SysGenConfig内置配置:jyx2/Assets/XLua/Src/GenAttributes.cs
  • 静态列表配置示例:jyx2/Assets/XLua/Examples/ExampleGenConfig.cs
  • 动态列表、自动化配置与黑名单示例:jyx2/Assets/XLua/Editor/ExampleConfig.cs
  • 免 GC(GCOptimize + CSharpCallLua)完整示例:jyx2/Assets/XLua/Examples/05_NoGc/NoGc.cs
  • 项目实战:CSharpCallLua 接口解读 Lua 战斗配置表:jyx2/Assets/Scripts/LuaCore/Jyx2LuaToCsBridge.cs
  • Lua 侧配置数据与初始化:jyx2/Assets/LuaScripts、jyx2/Assets/Mods/JYX2

【免费下载链接】jynewJinYongLegend-like RPG Game Framework with full Modding support and 10+ hours playable samples of game.项目地址: https://gitcode.com/GitHub_Trending/jy/jynew

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询