'yield self'和instance_eval一样吗?

时间:2009-09-15 03:56:26

标签: ruby

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

这是对的吗?

3 个答案:

答案 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类的所有实例添加一个私有方法。