文档编程语言【免费下载链接】cangjie_docs仓颉编程语言开发者文档。项目地址https://gitcode.com/Cangjie/cangjie_docs点击查看免费下载仓颉Cangjie编程语言将反射作为核心动态特性开发者可通过标准库std.reflect在运行时获取任意类型的类型信息、枚举并修改其成员变量与属性、动态调用函数从而优雅地实现通用框架、序列化、依赖注入等场景。本文将围绕TypeInfo类型族系统讲解如何获取类型信息、按完全限定名查询类型、通过反射读写成员变量与属性、动态调用静态函数并结合注解元数据与编译器反射选项给出完整的工程实践参考。读完本文你将能独立编写基于仓颉反射的可运行示例并理解反射带来的灵活性与性能代价之间的取舍。反射概述仓颉的动态特性从何而来反射Reflection是一种让程序在运行时访问、检测和修改其自身状态或行为的机制。仓颉语言将其与注解机制一同归入反射和注解章节共同构成语言层面的元编程能力。使用反射可以带来三方面的收益提高程序的灵活性和扩展性通用逻辑不必针对每种具体类型单独编写框架代码可以一次编写、处处适配。运行时获悉对象类型并操作其成员程序能够在运行时获得各种对象的类型并对其成员进行枚举、取值、赋值和调用等操作。运行时创建新类型无需提前硬编码配合类型信息与注解元数据可以在不修改源码的前提下扩展行为。不过需要清醒认识的是反射调用的性能通常低于直接调用。由于动态分派、类型检查与间接寻址的开销反射机制主要应用于对灵活性和扩展性要求很高的系统框架如依赖注入容器、ORM、序列化框架、测试框架上而热路径上的业务代码仍应优先使用静态调用。获取 TypeInfo反射的入口仓颉反射的核心类型是TypeInfo。这个类型记录了任意类型的类型信息并定义了获取类型信息、设置值等操作方法。为了便于操作仓颉标准库还提供了一系列信息类型包括ClassTypeInfo类的类型信息提供访问构造函数、成员变量、属性、函数的接口PrimitiveTypeInfo基础类型如Int64的类型信息ParameterInfo函数或构造函数的参数信息以及属性信息、成员变量信息、函数信息、注解信息等配套类型。要获得一个类型的TypeInfo最直接的方式是使用三种静态of方法public class TypeInfo { public static func of(a: Any): TypeInfo public static func of(a: Object): ClassTypeInfo public static func ofT(): TypeInfo }三种形式的区别在于入参为Any的of(a: Any)输出该实例的运行时类型信息入参为Object的of(a: Object)输出ClassTypeInfo更具体的类信息子类型泛型形式ofT()返回类型参数T的静态类型信息。以Any/Object实例为入参和以泛型参数调用得到的类型信息内容完全相同但两者无法保证对应同一对象——前者基于运行时实例推导后者基于编译期静态类型推导。下面用反射获取一个自定义类型的类型信息import std.reflect.* class Foo {} main() { let a: Foo Foo() let info: TypeInfo TypeInfo.of(a) let info2: TypeInfo TypeInfo.ofFoo() println(info) println(info2) }编译并执行输出default.Foo default.Foo示例中default.Foo中的default是未显式声明包时的默认 root 包名输出符合仓颉包与模块管理中包名需反映源文件相对于src的路径的规则。按名称查询类型TypeInfo.get 与完全限定名规则除of之外TypeInfo还提供了静态函数get可通过传入的类型名称字符串获取TypeInfopublic class TypeInfo { public static func get(qualifiedName: String): TypeInfo }调用时传入的参数需要符合module.package.type的完全限定模式规则。结合仓颉的模块结构root 包及其子包构成一棵树即一个模块完全限定名的各部分对应关系如下module模块名即模块 root 包名package类型所在的包路径由.分隔type类型名。例如在包含std.socket包与net.http包的场景下合法的查询形如let t1: TypeInfo TypeInfo.get(Int64) let t2: TypeInfo TypeInfo.get(default.Foo) let t3: TypeInfo TypeInfo.get(std.socket.TcpSocket) let t4: TypeInfo TypeInfo.get(net.http.ServerBuilder)需要注意的特殊规则对于编译器预导入的类型包括core包中的类型和编译器内置类型如primitive type、Option、Iterable等查找字符串需要直接使用其类型名不能带包名和模块名前缀例如Int64而不是core.Int64。当运行时无法查询到对应类型的实例时会抛出InfoNotFoundException。该方法不能获取未实例化的泛型类型。看下面的例子import std.collection.* import std.reflect.* class AT { A(public let t: T) {} } class BT { B(public let t: T) {} } main() { let aInfo: TypeInfo TypeInfo.get(default.AInt64) // Errordefault.AInt64 未实例化抛出 InfoNotFoundException let b: BInt64 BInt64(1) let bInfo: TypeInfo TypeInfo.get(default.BInt64) // OKdefault.BInt64 已被实例化 }即只有被实例化过的泛型类型才可以通过get查询到AInt64从未被实例化直接查询会抛异常而BInt64已经在main中实例化因而可以成功查询。使用反射访问成员变量getValue 与 setValue获得TypeInfo之后就可以通过其接口访问对应类的实例成员和静态成员。需要强调的是仓颉的反射被设计为只能访问类型内public的成员private和protected修饰的成员在反射中不可见这保证了封装边界不会因反射而被随意突破。在运行时获取并修改类的实例成员变量示例如下import std.reflect.* public class Foo { public static var param1 20 public var param2 10 } main(): Unit { let obj Foo() let info TypeInfo.of(obj) let staticVarInfo info.getStaticVariable(param1) let instanceVarInfo info.getInstanceVariable(param2) println(成员变量初始值) print(Foo 的静态成员变量 ${staticVarInfo} ) println((staticVarInfo.getValue() as Int64).getOrThrow()) print(obj 的实例成员变量 ${instanceVarInfo} ) println((instanceVarInfo.getValue(obj) as Int64).getOrThrow()) println(更改成员变量) staticVarInfo.setValue(8) instanceVarInfo.setValue(obj, 25) print(Foo 的静态成员变量 ${staticVarInfo} ) println((staticVarInfo.getValue() as Int64).getOrThrow()) print(obj 的实例成员变量 ${instanceVarInfo} ) println((instanceVarInfo.getValue(obj) as Int64).getOrThrow()) return }编译并执行输出成员变量初始值 Foo 的静态成员变量 static param1: Int64 20 obj 的实例成员变量 param2: Int64 10 更改成员变量 Foo 的静态成员变量 static param1: Int64 8 obj 的实例成员变量 param2: Int64 25几个关键 API 的使用要点getStaticVariable(name)/getInstanceVariable(name)分别按名称获取静态/实例成员变量的信息对象getValue()读取静态成员变量的值无需传入对象getValue(obj)读取实例成员变量的值需要传入目标对象实例setValue(newValue)/setValue(obj, newValue)分别写入静态/实例成员变量的新值返回值为Option类型通过as Int64显式转换后再getOrThrow()取出避免动态类型转换失败时程序静默出错。使用反射访问属性instanceProperties 与 isMutable仓颉的属性prop通过 getter 和可选的 setter 间接访问数据与普通变量用法一致但内部实现了访问控制与封装。反射同样支持对属性进行检查和修改import std.reflect.* public class Foo { public let _p1: Int64 1 public prop p1: Int64 { get() { _p1 } } public var _p2: Int64 2 public mut prop p2: Int64 { get() { _p2 } set(v) { _p2 v } } } main(): Unit { let obj Foo() let info TypeInfo.of(obj) let instanceProps info.instanceProperties.toArray() println(obj的实例成员属性包含${instanceProps}) let PropInfo1 info.getInstanceProperty(p1) let PropInfo2 info.getInstanceProperty(p2) println((PropInfo1.getValue(obj) as Int64).getOrThrow()) println((PropInfo2.getValue(obj) as Int64).getOrThrow()) if (PropInfo1.isMutable()) { PropInfo1.setValue(obj, 10) } if (PropInfo2.isMutable()) { PropInfo2.setValue(obj, 20) } println((PropInfo1.getValue(obj) as Int64).getOrThrow()) println((PropInfo2.getValue(obj) as Int64).getOrThrow()) return }编译并执行输出obj 的实例成员属性包含[prop p1: Int64, mut prop p2: Int64] 1 2 1 20该示例展示了反射访问属性的完整模式instanceProperties是TypeInfo暴露的实例属性集合可调用.toArray()枚举出该类型的所有实例属性getInstanceProperty(p1)/getInstanceProperty(p2)按名称获取单个属性信息getValue(obj)读取属性当前值isMutable()是修改前的必要检查只有mut修饰的属性才具备 setter对应仓颉属性语法中的mut prop不可变属性只含 getter 的prop调用setValue无意义。示例中p1为只读属性其值保持 1 不变p2为可变属性成功被改为 20。使用反射进行函数调用getStaticFunction 与 apply反射同样支持在运行时定位并调用函数。以静态函数为例import std.reflect.* public class Foo { public static func f1(v0: Int64, v1: Int64): Int64 { return v0 v1 } } main(): Unit { var num 0 let intInfo TypeInfo.ofInt64() let funcInfo TypeInfo.ofFoo().getStaticFunction(f1, intInfo, intInfo) num (funcInfo.apply(TypeInfo.ofFoo(), [1, 1]) as Int64).getOrThrow() println(num) }编译并执行输出2调用链解析如下TypeInfo.ofInt64()取得参数类型Int64的TypeInfoTypeInfo.ofFoo().getStaticFunction(f1, intInfo, intInfo)按函数名和形参类型列表定位静态函数f1——因为函数可能重载仅凭名称不足以唯一定位必须同时给出每个形参的类型信息funcInfo.apply(TypeInfo.ofFoo(), [1, 1])执行函数第一个参数是声明该函数的类型Foo的TypeInfo第二个参数是实参数组返回值同样以Option形式返回经as Int64转换后getOrThrow()取出。在此基础上ClassTypeInfo作为TypeInfo的子类还提供用于访问类公开构造函数以及成员变量、属性、函数的接口可以实现按名查成员、按成员取对象的通用框架能力例如先通过构造函数信息创建实例再通过实例成员信息批量注入属性。反射与注解的联动findAnnotation反射的价值在与注解元数据结合时进一步放大。自定义注解机制允许开发者通过Annotation标注的class定义带const init的注解类型注解信息在编译期生成并绑定到类型上之后由反射在运行时读取package pkg import std.reflect.TypeInfo Annotation public class Version { let code: String const init(code: String) { this.code code } } Version[1.0] class A {} Version[1.1] class B {} main() { let objects [A(), B()] for (obj in objects) { let annOpt TypeInfo.of(obj).findAnnotationVersion() if (let Some(ann) - annOpt) { println(ann.code) } } }输出1.0 1.1这里TypeInfo.of(obj).findAnnotationVersion()返回OptionVersion配合模式匹配if (let Some(ann) - annOpt)解包。需要注意注解元数据不会被继承一个类型的注解只来自它定义时声明的注解若要获取父类型的注解需自行沿类型层级用反射接口查询。二者结合即可支撑版本标记、权限校验、配置注入等复杂逻辑这正是类型元数据之外提供更多有用信息的设计初衷。编译与运行实践反射的工程约束用 cjc / cjpm 编译运行上述示例均可直接使用仓颉编译器cjc编译执行工具链安装见安装仓颉工具链编译器用法见cjc使用$ cjc reflect_demo.cj $ ./main对于多包、多文件的项目更推荐使用官方项目管理工具cjpm见cjpm介绍cjpm init初始化模块cjpm build构建cjpm run编译并运行。cjpm.toml中的name字段即模块名与TypeInfo.get完全限定名中的module段相对应。反射信息与产物体积--disable-reflection反射能力依赖编译期生成的反射信息。cjc提供了--disable-reflection编译选项详见cjc编译选项关闭反射选项后编译过程中不生成相关反射信息配合--lto[full|thin]使用时还会在 LTO 链接阶段清理导入库中残留的反射信息删除方法反射等无用数据同时保留字段名、枚举构造器等必要元数据从而减小最终产物体积注意两个边界场景交叉编译至aarch64-linux-ohos目标时默认关闭反射信息、该选项不生效当程序直接或间接链接/使用std.reflect反射 API如TypeInfo.of时为保证运行时反射功能正常LTO 场景下不会清理导入库中的反射信息。据此可以总结出工程实践上的取舍策略不依赖反射的产物开启--disable-reflection减小体积依赖反射的框架代码保持反射信息完整并把反射调用收敛到初始化、配置加载等低频路径上热路径仍然走静态调用。性能与安全提示反射的取值、赋值与函数调用均涉及运行时类型检查与间接寻址性能低于直接调用应在框架层使用、避免在循环热路径滥用反射只能访问public成员private/protected成员对反射不可见因此动态注入前应确认目标成员确实公开名称字符串成员名、完全限定名不会在编译期被校验拼写错误会在运行时以异常暴露建议在框架中对成员查找失败给出清晰的错误包装例如捕获InfoNotFoundException并补充上下文日志。小结仓颉的反射体系以TypeInfo为入口配合ClassTypeInfo、PrimitiveTypeInfo、ParameterInfo等类型族实现了取类型、查成员、读写属性、调函数、读注解的完整动态能力闭环。of三种重载与get分别对应实例推导、静态推导、名称查询三条获取路径完全限定名规则与泛型实例化限制决定了get的使用边界getValue/setValue/apply等接口则提供了统一的成员操作原语。若需要更进一步可阅读注解章节了解Annotation自定义注解与target位置约束并结合常量求值理解注解参数为何必须是const表达式——二者共同构成了仓颉面向框架开发的元编程工具箱。赞分享文档编程语言【免费下载链接】cangjie_docs仓颉编程语言开发者文档。项目地址https://gitcode.com/Cangjie/cangjie_docs点击查看免费下载相关推荐仓颉编程语言反射机制详解从 TypeInfo 获取到成员动态访问实战仓颉编程语言反射机制详解从 TypeInfo 获取到成员动态访问实战 反射是仓颉编程语言Cangjie动态特性的核心它让程序能够在运行时访问、检测和修改文档编程语言GlazeWM源码解析(三)PendingSync待同步机制让状态高效渲染到屏幕GlazeWM源码解析 三 PendingSync待同步机制让状态高效渲染到屏幕 GlazeWM 是一款为 macOS 和 Windows 打造的平铺窗口管理编程语言语言运行时标准库垃圾回收并发编程仓颉编程语言反射入门使用 TypeInfo 获取与解析运行时类型信息仓颉编程语言反射入门使用 TypeInfo 获取与解析运行时类型信息 在仓颉编程语言Cangjie中 std.reflect 包提供了运行时反射能力其文档知识库教程OpenHarmony创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考