Crystal语言中如何持有类引用以实现后续动态实例化?
解决Crystal中传递类引用并实例化的问题
嘿,我懂你这会儿遇到的困扰——Crystal作为静态类型语言,对实例变量的类型要求很严格,直接用Class确实会因为太宽泛报错。不过咱们有几个靠谱的办法能解决这个需求,而且还能保持类型安全:
方法一:使用泛型(最推荐)
泛型是Crystal处理这类“任意但有约束的类型”场景的最佳实践,它能让编译器明确知道你要操作的类的类型特征:
class Route(T) # 明确指定存储的是T类的引用 getter action : T.class def initialize(@action : T.class) end def do_something # 编译器能确认T的实例有call方法 @action.new.call end end class Action def call puts "called" end end # 两种写法都可以,Crystal能自动推断泛型类型 route = Route.new(Action) # 或者显式指定:route = Route(Action).new(Action) route.do_something # 输出 "called"
这种方式的好处是完全保留了编译时的类型检查,只要传入的类有call方法,编译器就不会报错;如果传入的类没有call,编译阶段就能发现问题。
方法二:用模块做类型约束
如果希望Route能接受所有实现了特定方法的类,可以先定义一个模块来统一约束:
# 定义一个模块,要求实现call方法 module CallableAction abstract def call end class Route # 指定action必须是包含CallableAction模块的类 getter action : CallableAction.class def initialize(@action : CallableAction.class) end def do_something @action.new.call end end class Action # 包含模块,满足约束 include CallableAction def call puts "called" end end # 其他符合约束的类也能传入 class AnotherAction include CallableAction def call puts "another called" end end route1 = Route.new(Action) route1.do_something # 输出 "called" route2 = Route.new(AnotherAction) route2.do_something # 输出 "another called"
这种方式适合你需要支持多个不同类,但它们都有共同方法的场景,既灵活又能保证类型安全。
方法三:类型断言(不推荐,应急用)
如果你真的需要完全不限制类型,愿意放弃部分编译时检查,可以用类型断言的方式:
class Route getter action : Class(Reference) def initialize(@action) end def do_something # 断言实例有call方法,运行时如果没有会报错 action_instance = @action.new.as({ def call; end }) action_instance.call end end class Action def call puts "called" end end route = Route.new(Action) route.do_something # 输出 "called"
这种方式的缺点是丢失了编译时的类型检查,如果传入的类没有call方法,只有运行时才会报错,所以除非万不得已,优先用前两种方法。
内容的提问来源于stack exchange,提问作者Thiago Felippe
相关产品推荐
相关产品推荐

