简体   繁体   中英

Ruby's def and instance_eval vs. class_eval

I'm reading the Metaprogramming section of Programming Ruby 1.9 and I'm having trouble understanding what's going on internally between class_eval / class_exec vs. instance_eval / instance_exec .

So first of all, my understanding is that def adds a method to the method table of self (the class object):

class A
  puts self  # => A
  def foo; 42; end  # added to the method table of self, so becomes an instance method
end
A.new.foo  # => 42

And if we use class_eval , we get the same behavior:

A.class_eval do
  puts self  # => A
  def bar; 42; end  # same as above
end
A.new.bar  # => 42

But somehow in the instance_eval case, things are different:

A.instance_eval do
  puts self  # => A
  def baz; 42; end  # added to the method table of an anonymous
                    # singleton class of self, so becomes a class method
end
puts A.baz  # => 42

s = 'string'
s.instance_eval do ... end  # same behavior, so now def creates an instance method

So I understand the functional difference between class_eval and instance_eval .

But the contexts inside the class_eval and instance_eval blocks look exactly the same to me -- in particular, self points to the same object, and the local_variables are the same. So what's going on inside the blocks (internally) that's making def act different?

Is there some piece of documentation I could read? The RDoc for instance_eval and class_eval doesn't help. Looking at the source, instance_eval seems to set up a singleton class object whereas class_eval doesn't -- but is this difference visible outside the C code, on the Ruby level?

I think your confusion comes from the fact that def does not depend on the current self, you might think about it as being a "current class" that has it's own rules.

Following your examples:

class A
  # defs here go to A
  puts self  # => A
  class << self
     #defs here go to A's eigenclass
  end
end

A.class_eval do
  #defs here go to A
end

A.instance_eval do
  #defs here go to A's eigenclass     
end

s = "Hello World"

class << s
  #defs here go to s's eigenclass
end

Here's the portion of the chapter that talks about the issue and it's pretty clear about the behaviour

class_eval and instance_eval both set self for the duration of the block. However, they differ in the way they set up the environment for method definition. class_eval sets things up as if you were in the body of a class definition, so method definitions will define instance methods In contrast, calling instance_eval on a class acts as if you were working inside the singleton class of self. Therefore, any methods you define will become class methods.

The only thing I think is worth adding is that you can call instance_eval in any object, not just classes, and the behaviour doesn't change but has different consequences.

Some relevant reading:

Ruby: instance_eval and class_eval method definitions

Chapter 4 of this most excelent series

Just to add to @krusty.ar's answer: def and define_method add methods to the current method definition context (I believe that's what it's called, I'm not sure), not to the current self .

It's just that inside of a module, class or singleton class body, those two happen to be the same.

But, for example, in a script body (aka top-level), self is the main object, but the current method definition context is Object .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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