Является ли "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.
Правильно ли это?
Ответы
Ответ 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.