Ruby类中锁定类变量的方法及相关代码疑问解答
Ruby类变量锁定相关问题解答
1. 上述代码是否意味着@@states已被冻结?
是的,但要明确:@@states指向的OpenStruct实例被冻结了——代码里调用了.freeze,会阻止对该对象的修改(比如添加/修改OpenStruct的属性)。但冻结的是对象本身,不是@@states这个变量,变量仍然可以被重新赋值。
2. @@states是否可以重新赋值?若可以,如何操作?
可以重新赋值。类变量@@states的作用域覆盖整个类及其子类,只要在类的上下文中直接赋值即可,比如:
# 方式1:在类定义外部通过class_eval修改 Test.class_eval do @@states = OpenStruct.new({finished: 'finished'}).freeze end # 方式2:直接在类定义内部修改 class Test @@states = 'new value' end
只要能进入类的上下文,就能修改这个类变量。
3. 在类方法中使用@states和@@states是否相同?
完全不同:
@@states是类变量,作用域覆盖整个类及其所有子类,所有子类共享这个变量的值。@states是类实例变量,它属于Test这个类对象本身(Ruby中类也是对象),子类不会继承这个变量,每个类对象有自己独立的类实例变量。
示例代码:
class Test @states = '类实例变量' @@states = '类变量' def self.show_vars puts "@states: #{@states}" puts "@@states: #{@@states}" end end class SubTest < Test; end Test.show_vars # 输出: # @states: 类实例变量 # @@states: 类变量 SubTest.show_vars # 输出: # @states: # @@states: 类变量
4. 若@@states未被冻结或可重新赋值,如何在其被设置后锁定使其无法重新赋值?
Ruby没有直接锁定类变量禁止赋值的语法,但有两种稳妥的实现方式:
方案1:用类实例变量+只读属性替代类变量(推荐)
这种方式可以让变量只能被读取,常规操作下无法重新赋值:
class Test class << self attr_reader :states end # 直接初始化 @states = OpenStruct.new({initial: 'initial', started: 'started'}).freeze # 若要延迟初始化,用类方法: # def self.states # @states ||= OpenStruct.new(...).freeze # end end
方案2:移除类变量的赋值方法(hack方式,不推荐)
通过元编程移除类的@@states=方法,阻止常规赋值:
class Test def self.states @@states ||= OpenStruct.new({initial: 'initial', started: 'started'}).freeze end # 初始化后移除赋值方法 class << self remove_method(:@@states=) end end
不过这种方式依然可以通过class_eval等元编程手段绕过,所以更推荐第一种方案。
内容的提问来源于stack exchange,提问作者Ror Roars
相关产品推荐
相关产品推荐

