gpt4 book ai didi

ruby - 在 Ruby 中通过 instance_eval 调用 block 时,动态添加的访问器分配不起作用

转载 作者:数据小太阳 更新时间:2023-10-29 08:41:22 25 4
gpt4 key购买 nike

我有一个类,我在运行时动态地向其添加属性访问器。此类构成 DSL 的一部分, block 由此传递给配置方法并使用 instance_eval 调用。这使得在引用类的方法时可以在 DSL 中删除对“self”的引用。

但是,我发现我可以引用属性来检索它们的值,但无法分配它们,除非明确引用自身,如以下代码示例所示。

class Bar

def add_dynamic_attribute_to_class(name)
Bar.add_dynamic_attribute(name)
end

def invoke_block(&block)
instance_eval &block
end

def self.add_dynamic_attribute(name)
attr_accessor name
end

end

b = Bar.new

b.add_dynamic_attribute_to_class 'dyn_attr'

b.dyn_attr = 'Hello World!'

# dyn_attr behaves like a local variable in this case
b.invoke_block do
dyn_attr = 'Goodbye!'
end

# unchanged!
puts "#{b.dyn_attr} but should be 'Goodbye!'"

# works if explicitly reference self
b.invoke_block do
self.dyn_attr = 'Goodbye!'
end

# changed...
puts "#{b.dyn_attr} = 'Goodbye!"

# using send works
b.invoke_block do
send 'dyn_attr=', 'Hello Again'
end

# changed...
puts "#{b.dyn_attr} = 'Hello Again!"

# explain this... local variable or instance method?
b.invoke_block do

puts "Retrieving... '#{dyn_attr}'"

# doesn't fail... but no effect
dyn_attr = 'Cheers'

end

# unchanged
puts "#{b.dyn_attr} should be 'Cheers'"

谁能解释为什么这不符合预期?

最佳答案

问题出在 Ruby 处理实例变量和局部变量的方式上。发生的情况是您在 instance_eval block 中设置了一个局部变量,而不是使用 ruby​​ 访问器。

这可能有助于解释:

class Foo
attr_accessor :bar

def input_local
bar = "local"
[bar, self.bar, @bar, bar()]
end

def input_instance
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end

def input_both
bar = "local"
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end
end

foo = Foo.new
foo.input_local #["local", nil, nil, nil]
foo.input_instance #["instance", "instance", "instance", "instance"]
foo.input_both #["local", "instance", "instance", "instance"]

bocks 的工作方式是它们区分局部变量和实例变量,但是如果在调用读取器时未定义局部变量,则该类默认为实例变量(就像我在调用 input_instance 时的情况一样)示例)。

可通过三种方式获得所需的行为。

使用实例变量:

    class Foo      attr_accessor :bar      def evaluate(&block)        instance_eval &block      end    end    foo = Foo.new    foo.evaluate do      @bar = "instance"    end    foo.bar #"instance"

使用自变量:

    class Foo      attr_accessor :bar      def evaluate(&block)        block.call(self)      end    end    foo = Foo.new    foo.evaluate do |c|      c.bar = "instance"    end    foo.bar #"instance"

使用setter函数:

    class Foo      attr_reader :bar      def set_bar value        @bar = value      end      def evaluate(&block)        instance_eval &block      end    end    foo = Foo.new    foo.evaluate do      set_bar "instance"    end    foo.bar #"instance"

所有这些示例都将 foo.bar 设置为“instance”。

关于ruby - 在 Ruby 中通过 instance_eval 调用 block 时,动态添加的访问器分配不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4403973/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com