Подтвердить что ты не робот

Является ли "yield self" таким же, как instance_eval?

Есть ли разница, если вы определяете Foo с помощью instance_eval:.,.

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

., или с "yield self": ​​

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.

Правильно ли это?

4b9b3361

Ответ 1

Ваши две части кода очень разные. Используя instance_eval, вы оцениваете блок в контексте вашего объекта. Это означает, что использование def будет определять методы для этого объекта. Это также означает, что вызов метода без получателя внутри блока вызовет его на ваш объект.

Когда вы получаете self, вы передаете себя как аргумент блоку, но поскольку ваш блок не принимает никаких аргументов, он просто игнорируется. Поэтому в этом случае уступка "Я" делает то же самое, что ничего не дает. def здесь ведет себя точно так же, как a 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 заключается в том, что вы должны явно указать получателя.

Изменить, чтобы уточнить:

В версии с выходом obj в блоке будет получен объект, который в этом случае является вновь созданным экземпляром Foo. В то время как self будет иметь то же значение, что и вне блока. С версией instance_eval self внутри блока будет вновь созданный экземпляр Foo.

Ответ 2

Они разные. 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

Ответ 3

Вы можете оставить ключевое слово

class Foo
  def initialize
    yield if block_given?
  end
end

Обновление от комментариев

Используя yield, я немного новичок в своем вкусе, особенно когда используется вне 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.