Raku如何实现类工厂动态生成类?解决未声明名称报错
问题原因
^set_name方法仅修改元对象内部存储的类名字段,不会自动将类注册到Raku的符号表中。Raku中所有可以直接通过名称访问的类、子程序、变量,都需要在编译期或运行期显式绑定到对应作用域的符号表,未做绑定操作时编译器自然无法识别新类名。
测试代码修正
在调用^set_name后,显式将类绑定到对应作用域的符号表即可通过新类名访问:
constant A := Metamodel::ClassHOW.new_type( name => 'A' ); A.^add_method('x', my method x(A:) { say 42 }); A.^add_method('set', my method set(A: Str:D $name) { self.^set_name($name); # 绑定到全局符号表,全局可通过类名直接访问 GLOBAL::{$name} := self; }); A.^compose; my $bar = A; $bar.set('Foo'); say $bar.^name; # 输出 Foo A.x; # 正常输出42 Foo.x; # 正常输出42
批量生成Dimension子类的工厂实现
针对你批量生成结构一致的单位类的需求,不需要动态修改类名,直接在创建类时传入最终类名、添加方法、完成类组合后绑定到符号表即可,实现更稳定:
# 所有待生成单位的配置数据:[类名/缩写, 乘子分子, 乘子分母] my @mass-units = [ 'kg', 1.0, Quantity.new(1000.0, 'g'), 'mg', 1000.0, Quantity.new(1.0, 'g'), 'g', 1.0, 1.0, 't', 0.001, Quantity.new(1.0, 'kg') ]; # 类工厂函数 sub generate-dimension-class(Str:D $name, Numeric $num, Quantity|Numeric $den) { # 创建类,直接指定父类为Dimension、类名为传入名称 my $class := Metamodel::ClassHOW.new_type( :$name, :is(Dimension) ); # 添加new方法 $class.^add_method('new', my method new($class: ) { $class.bless( :type('mass'), :abbr($name), :multiplier(Multiplier.new( numerator => $num, denominator => $den ~~ Quantity ?? $den !! Quantity.new($den, 'g') )) ) }); # 完成类组合 $class.^compose; # 绑定到全局符号表 GLOBAL::{$name} := $class; return $class; } # 批量生成所有单位类 for @mass-units -> ($name, $num, $den) { generate-dimension-class($name, $num, $den); } # 直接通过类名调用 say kg.new.abbr; # 输出 kg say mg.new.multiplier.numerator; # 输出 1000
注意事项
- 如果不需要类全局可见,不要将类安装到
GLOBAL::符号表,替换为对应业务包的符号表即可,例如包名为Unit::Mass时,使用Unit::Mass::{$name} := $class绑定。 - 类必须在调用
^compose完成元对象组合之后再安装到符号表,否则会出现方法未定义、继承不生效等异常。 - 不建议在类组合完成后调用
^set_name修改类名,直接在创建类时传入最终类名是更稳定的实现方式。
内容的提问来源于stack exchange,提问作者StevieD
相关产品推荐
相关产品推荐

