【问题标题】:How to know when an instance variable gets set in Ruby如何知道实例变量何时在 Ruby 中设置
【发布时间】:2026-01-07 18:45:02
【问题描述】:

有没有办法覆盖 Ruby 中实例变量的设置?

假设我设置了一个实例变量:

@foo = "bar"

我可以截取它并做一些事情吗(例如记录或放置)

我想,我正在尝试覆盖所有类型的赋值运算符。这也能做到吗?

到目前为止,我想出的最好的是:

class Module
  def attr_log_accessor( *symbols )
    symbols.each { | symbol |
      module_eval( "def #{symbol}() @#{symbol}; end" )
      module_eval( "def #{symbol}=(val) 
                      @#{symbol} = val
                      puts \"#{symbol} has changed\"
                    end" )
    }
  end
end

然后,当我定义访问器并设置它时,我的代码就会被执行:

class Test
  attr_log_accessor :foo

  def DoSomething
    self.foo = "bar"
  end
end

不幸的是,这需要我写 self.foo = "bar",而不是 @foo = "bar"。

有什么想法吗?

【问题讨论】:

标签: ruby


【解决方案1】:

不,您不能直接执行此操作。最好的方法是仅通过 getter/setter 方法访问实例变量并覆盖它们。

【讨论】:

    【解决方案2】:

    你可以乱用instance_variable_getinstance_variable_setinstance_variables。更多内容可以在这篇Ruby's Metaprogramming Toolbox 文章中找到。

    如果您能描述为什么要这样做,我们或许可以提供更好的方法。

    【讨论】:

    • 我正在尝试这样做,因为对于类上的特定属性,我想在属性更改时通知其他对象。更具体地说,这是为了与 .Net 集成。我正在使用 IronRuby 并在 INotifyPropertyChanged.PropertyChanged 事件上触发事件。我想将一个属性声明为可通知的:attr_notifiable :foo 或其他东西,只要设置了@foo,它就会触发事件。最终,self.foo 会起作用,但如果我能在底层进入(分配给本地 var),那么它对我来说会更有用。
    【解决方案3】:

    你可以使用method_missing()来做到这一点

    示例:

    def method_missing(name, *args)
      attribute = name.to_s
      if attribute =~ /=$/
        @attributes[attribute.chop] = args[0]
      else
        @attributes[attribute]
      end
    end
    

    如果你的 getter/setter 中有处理,这个方法很快就会变得臃肿。

    有关更多信息,请查看 OpenStruct 源代码。另外,我从Metaprogramming Ruby (pg 52) 中提取了这段代码

    另外,请注意,这将捕获所有缺失的方法。如果您只想拦截obj.[*]=,那么您必须将@attributes[attribute]更改为super

    【讨论】: