'yield self'和instance_eval一样吗?

uzo*_*uzo 20 ruby

如果用instance_eval定义Foo,有什么区别吗:..

class Foo
    def initialize(&block)
      instance_eval(&block) if block_given?
    end
  end
Run Code Online (Sandbox Code Playgroud)

...或者'屈服自我':

class Foo
  def initialize
    yield self if block_given?
  end
end
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,您都可以这样做:

x = Foo.new { def foo; 'foo'; end }
x.foo 
Run Code Online (Sandbox Code Playgroud)

所以' yield self'意味着Foo.new之后的块总是在Foo类的上下文中进行评估.

它是否正确?

sep*_*p2k 15

你的两段代码做了很多不同的事情.通过使用instance_eval,您将在对象的上下文中评估块.这意味着使用def将定义该对象的方法.它还意味着在块内调用没有接收器的方法将在您的对象上调用它.

在屈服于self时,你会将self作为参数传递给块,但由于你的块不带任何参数,所以它被忽略了.所以在这种情况下,屈服于自我就会产生同样的东西.在def这里表现完全像def块外会,产生自实际上并没有改变什么,你定义的方法.你能做的是:

class Foo
  def initialize
    yield self if block_given?
  end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo
Run Code Online (Sandbox Code Playgroud)

与instance_eval的区别在于您必须明确指定接收方.

编辑澄清:

在带有yield的版本中,块中的obj将是生成的对象,在这种情况下是新创建的Foo实例.虽然self将具有与块外部相同的值.使用self块内的instance_eval版本将是新创建的Foo实例.


Aug*_*aas 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
Run Code Online (Sandbox Code Playgroud)

  • 1.8.7打印Foo实例,因为self作为未记录的参数传递给1.8.7中的instance_eval.修订版9937(http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/eval.c?r1=9936&r2=9937&diff_format=h)中删除了此未记录的参数,并重新添加修订版10063(http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/ruby_1_8_7/eval.c?r1=10047&r2=10063&diff_format=h)及时将其放入树中1.8.7 (http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/tags/v1_8_7/eval.c?annotate=16730&diff_format=h) (3认同)

khe*_*lll 5

你可以删除self关键字

class Foo
  def initialize
    yield if block_given?
  end
end
Run Code Online (Sandbox Code Playgroud)

从评论更新

使用产量对我来说有点新鲜,特别是在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>
Run Code Online (Sandbox Code Playgroud)

检查一下:

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"
Run Code Online (Sandbox Code Playgroud)

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

  • "正如你可以看到的不同之处在于前者正在向正在初始化的对象添加单例方法foo,而后者则将该方法添加到类Foo2的所有实例中"实际上,后者是将方法添加到Object(和在真正的红宝石(而不是irb)中,它会将它添加为私有方法,因此你不能做x.foo或z.foo,只是foo).换句话说,def的行为就像你把它写在块之外一样(除非方法当然没有产生,在这种情况下没有任何反应). (2认同)