如果用instance_eval定义Foo有什么不同:。 。
class Foo
def initialize(&block)
instance_eval(&block) if block_given?
end
end
即可。 。 。或者“屈服自我”:
class Foo
def initialize
yield self if block_given?
end
end
在任何一种情况下,你都可以这样做:
x = Foo.new { def foo; 'foo'; end }
x.foo
所以'yield self
'意味着Foo.new之后的块总是在Foo类的上下文中计算。
这是对的吗?
答案 0 :(得分:15)
你的两段代码做了很多不同的事情。通过使用instance_eval,您将在对象的上下文中评估块。这意味着使用def将定义该对象的方法。这也意味着在块内调用没有接收器的方法会在你的对象上调用它。
当屈服于self时,你将self作为参数传递给块,但由于你的块没有接受任何参数,所以它被简单地忽略了。所以在这种情况下,屈服于自我就会产生同样的东西。 def
这里的行为与块外的def
完全相同,产生self并不会实际改变你定义方法的内容。你能做的是:
class Foo
def initialize
yield self if block_given?
end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo
与instance_eval的区别在于您必须明确指定接收器。
编辑以澄清:
在带有yield的版本中,块中的obj将是生成的对象,在这种情况下是新创建的Foo实例。虽然self将具有与块外部相同的值。使用块中的instance_eval版本self
将是新创建的Foo实例。
答案 1 :(得分:8)
他们是不同的。 yield(self)
不会更改块内self
的值,而instance_eval(&block)
则会更改。
class Foo
def with_yield
yield(self)
end
def with_instance_eval(&block)
instance_eval(&block)
end
end
f = Foo.new
f.with_yield do |arg|
p self
# => main
p arg
# => #<Foo:0x100124b10>
end
f.with_instance_eval do |arg|
p self
# => #<Foo:0x100124b10>
p arg
# => #<Foo:0x100124b10>
end
答案 2 :(得分:5)
您可以放弃自我关键字
class Foo
def initialize
yield if block_given?
end
end
从评论中更新
使用产量,我的口味有点新鲜,特别是在irb外使用时。
然而, instance_eval 方法与 yield 方法之间存在重大差异,请查看以下代码段:
class Foo
def initialize(&block)
instance_eval(&block) if block_given?
end
end
x = Foo.new { def foo; 'foo'; end }
#=> #<Foo:0xb800f6a0>
x.foo #=> "foo"
z = Foo.new #=> #<Foo:0xb800806c>
z.foo #=>NoMethodError: undefined method `foo' for #<Foo:0xb800806c>
同时检查一下:
class Foo2
def initialize
yield if block_given?
end
end
x = Foo2.new { def foo; 'foo'; end } #=> #<Foo:0xb7ff1bb4>
x.foo #=> private method `foo' called for #<Foo2:0xb8004930> (NoMethodError)
x.send :foo => "foo"
z = Foo.new #=> #<Foo:0xb800806c>
z.send :foo => "foo"
正如您所看到的,区别在于前一个方法是向正在初始化的对象添加单例方法 foo ,而后者正在向Object类的所有实例添加一个私有方法。