为什么你可以在'initialize'中调用instance_eval(而不是class_eval)?

时间:2009-09-14 17:03:41

标签: ruby

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

我想知道在这里假设与'initialize'一起使用的块的类型。

由于调用了instance_eval,这意味着在Observer类的上下文中计算了块。

为什么会这样做,而不是比如说class_eval,以及在类的上下文中评估块的结果可能是什么?

另外,如何调用它?

4 个答案:

答案 0 :(得分:9)

首先,你不能做这样的事情:

class Observer
  def initialize(&block)
    class_eval(&block) if block_given?
  end
end

因为没有为class_eval的实例定义Observer。它在Module中定义(Class下降)。我们稍后会回到class_eval

使用上述习语的原因通常是允许块初始化:

x = Observer.new do
  add_event(foo)
  some_other_instance_method_on_observer
  self.some_attribute = something
end

另外,您可以将方法添加到类的给定实例中:

foo = Observer.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"

如果没有instance_eval,你可以完成同样的事情:

class Foo
  def initialize
    yield self if block_given?
  end
end

foo = Foo.new do |x|
  x.add_event(foo)
  x.some_other_instance_method_on_observer
  x.self.some_attribute = something
end

但这并没有让你能够添加方法。如果你这样做:

foo = Foo.new do
  def foo
    'foo'
  end
end

foo.foo  # => "foo"

似乎有效,对吧?但是你实际做的是将foo方法添加到所有内容中,因为self设置为“主”对象。它等同于简单地定义块外的方法。它们作为实例方法添加到Object,因此它们可以处理所有事情。

现在,正如所承诺的那样,回到class_eval。你可以做这样的事情:

class Observer
  def initialize(&block)
    class.class_eval(&block) if block_given?
  end
end

然后你打开整个班级:

x = Observer.new { def foo; 'foo'; end }
x.foo                                      # => "foo"
y = Observer.new
y.foo                                      # => "foo"

这通常不是我们想要做的。另外,self将是类,而不是实例。这使得它对于块初始化毫无用处,如上所示。

答案 1 :(得分:2)

一个用例是在其上下文中设置观察者的状态。

也许

o = Observer.new do
      listen_to(<some object>)
      report_to(<something>)
    end

这样的使用不适用于class_eval,它只能访问类状态

答案 2 :(得分:0)

Initialize是一个实例方法。它是在实例的上下文中执行的,而不是类本身。因此instance_eval导致块也在实例的上下文中执行。

普通对象实例中没有class_eval这样的东西 - 它只是为类定义的。

答案 3 :(得分:0)

只是因为 initialize 方法是一个实例方法, class_eval 是为类型的对象定义的,这意味着它们只能在类方法或类体内部。

因此,以下代码段会引发错误:

  

“”。class_eval {methods}#=&gt;   NoMethodError:未定义的方法   `class_eval'代表“”:String

虽然这个会奏效:

s.class.class_eval{methods} #=> ["methods", "respond_to?", "module_eval", "class_variables", "dup", "instance_variables", "protected_instance_methods", "__id__", "public_method_defined?", "eql?", "object_id", "const_set", "id", "singleton_methods", "send", "class_eval", "taint", "include?", "private_instance_methods", "frozen?", "instance_variable_get", "private_method_defined?", "__send__", "instance_of?", "name", "to_a", "autoload", "type", "new", "protected_methods", "instance_eval", "display", "instance_method", "instance_variable_set", "kind_of?", "protected_method_defined?", "extend", "const_defined?", "to_s", "ancestors", "public_class_method", "allocate", "class", "<=>", "hash", "<", "tainted?", "private_methods", "==", "instance_methods", "===", "class_variable_defined?", ">", "nil?", "untaint", "constants", ">=", "is_a?", "autoload?", "<=", "inspect", "private_class_method", "const_missing", "method", "clone", "=~", "public_instance_methods", "public_methods", "method_defined?", "superclass", "instance_variable_defined?", "equal?", "freeze", "included_modules", "const_get"]