Ruby中能否在不传递参数的情况下为块定义base变量?
如何在不修改块定义的情况下为块提供
base变量 可以实现你的需求,但需要通过修改块执行时的上下文,让块里的base调用指向你想要传递的对象。先解释下你之前尝试的方法为什么无效:
block.instance_variable_set(:@base, base):你是给Proc对象本身设置实例变量,但块里的base是在查找当前self的局部变量或方法,和Proc对象的实例变量无关——只有块里写@base才会用到这个值,但你不想修改块的定义,所以这个方法没用。block.define_singleton_method(:hi):同样是给Proc对象添加方法,而块里的hi是调用当前self的方法,和Proc对象本身的方法无关,自然也不会生效。
可行解决方案
核心思路是让块执行时的self拥有一个base方法,返回你想要传递的base对象。这样块里的base.call_my_test就会调用这个方法,拿到目标对象后执行对应方法,完全不需要修改块的定义。
方法1:临时模块扩展对象
def test(&block) MyTest.with_base_object do |base| # 创建临时模块,定义base方法返回目标实例 context_module = Module.new do define_method(:base) { base } end # 创建包含该模块的对象,在其上下文中执行块 context_obj = Object.new.extend(context_module) context_obj.instance_eval(&block) end end
方法2:直接给临时对象定义单例方法(更简洁)
def test(&block) MyTest.with_base_object do |base| context_obj = Object.new # 给临时对象添加base方法,返回目标实例 context_obj.define_singleton_method(:base) { base } # 在临时对象的上下文中执行块 context_obj.instance_eval(&block) end end
原理说明
instance_eval(&block)会把块的执行上下文(即self)切换为context_obj,块里的所有方法调用都会优先在context_obj上查找。我们给context_obj定义的base方法,正好匹配块里的base调用,返回MyTest.with_base_object提供的实例,从而让base.call_my_test正常执行。
内容的提问来源于stack exchange,提问作者basic
相关产品推荐
相关产品推荐

