‘yield self’和instance_eval一样吗?

如果用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类的上下文中进行计算。

它是否正确?

你的两段代码做了很多不同的事情。 通过使用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实例。

它们是不同的。 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 # => # end f.with_instance_eval do |arg| p self # => # p arg # => # end 

你可以删除self关键字

 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 } #=> # x.foo #=> "foo" z = Foo.new #=> # z.foo #=>NoMethodError: undefined method `foo' for # 

检查一下:

 class Foo2 def initialize yield if block_given? end end x = Foo2.new { def foo; 'foo'; end } #=> # x.foo #=> private method `foo' called for # (NoMethodError) x.send :foo => "foo" z = Foo.new #=> # z.send :foo => "foo" 

正如您所看到的,区别在于前一个方法是向正在初始化的对象添加单例方法foo ,而后者则向Object类的所有实例添加一个私有方法。