简体   繁体   English

'yield self'和instance_eval一样吗?

[英]Is 'yield self' the same as instance_eval?

Is there any difference if you define Foo with instance_eval: . 如果用instance_eval定义Foo,有什么区别吗: . .

class Foo
    def initialize(&block)
      instance_eval(&block) if block_given?
    end
  end

. . . or with 'yield self': 或者'屈服自我':

class Foo
  def initialize
    yield self if block_given?
  end
end

In either case you can do this: 无论哪种情况,您都可以这样做:

x = Foo.new { def foo; 'foo'; end }
x.foo 

So ' yield self ' means that the block after Foo.new is always evaluated in the context of the Foo class. 所以' yield self '意味着Foo.new之后的块总是在Foo类的上下文中进行评估。

Is this correct? 它是否正确?

Your two pieces of code do very different things. 你的两段代码做了很多不同的事情。 By using instance_eval you're evaluating the block in the context of your object. 通过使用instance_eval,您将在对象的上下文中评估块。 This means that using def will define methods on that object. 这意味着使用def将定义该对象的方法。 It also means that calling a method without a receiver inside the block will call it on your object. 它还意味着在块内调用没有接收器的方法将在您的对象上调用它。

When yielding self you're passing self as an argument to the block, but since your block doesn't take any arguments, it is simply ignored. 在屈服于self时,你会将self作为参数传递给块,但由于你的块不带任何参数,所以它被忽略了。 So in this case yielding self does the same thing as yielding nothing. 所以在这种情况下,屈服于自我就会产生同样的东西。 The def here behaves exactly like a def outside the block would, yielding self does not actually change what you define the method on. 这里的def就像块外的def一样,屈服于self并不会实际改变你定义方法的内容。 What you could do is: 你能做的是:

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

The difference to instance_eval being that you have to specify the receiver explicitly. 与instance_eval的区别在于您必须明确指定接收方。

Edit to clarify: 编辑澄清:

In the version with yield, obj in the block will be the object that is yielded, which in this case is is the newly created Foo instance. 在带有yield的版本中,块中的obj将是生成的对象,在这种情况下是新创建的Foo实例。 While self will have the same value it had outside the block. 虽然self将具有与块外部相同的值。 With the instance_eval version self inside the block will be the newly created Foo instance. 使用instance_eval版本,块内的self将是新创建的Foo实例。

They are different. 它们是不同的。 yield(self) does not change the value of self inside the block, while instance_eval(&block) does. 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

You just can drop the self keyword 你可以删除self关键字

class Foo
  def initialize
    yield if block_given?
  end
end

Update from comments 从评论更新

Using yield there is a bit new to my taste, specially when used outside irb. 使用产量对我来说有点新鲜,特别是在irb外使用时。

However there is a big and significant difference between instance_eval approach and yield approach, check this snippet: 但是, 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>

Check this one as well: 检查一下:

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"

As you can see the difference is that the former one is adding a singleton method foo to the object being initialized, while the later is adding a private method to all instances of Object class. 正如您所看到的那样,区别在于前者正在向正在初始化的对象添加单例方法foo ,而后者则向Object类的所有实例添加私有方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM