简体   繁体   中英

Difference between instance_eval and class << self?

I can't seem to grasp the exact difference between these two "constructs". To my mind, the following small script should output the same thing three times:

class Example
  puts self

  class << self
    puts self
  end

  instance_eval do
    puts self
  end
end

However, the output is:

Example
#<Class:Example>
Example

Here's my rationale:

  • Example is an instance of Class , so self in the class body refers to that;
  • class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);
  • instance_eval runs the block in the given instance, so, in my case it's pretty much the same as putting the code in the block directly in the class body.

My current guess is that class << self inserts a ghost class between Example and Class and sets self to that, but the output of #<Class:Example> is not confirming that at all.

So what is wrong with my rationale?

class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);

No, class << obj opens up the singleton class of obj . As you correctly pointed out, inside of a class declaration, self refers to the class itself, so, in this case, the "inner" self (ie the one being passed to puts ) refers to the singleton class of Example .

In my opinion, class << self has been one of the most obnoxious bits of syntax in Ruby. People new to the language have little idea what it means, apart from cargo-cult conventions, and even those intimately familiar with the language have only a hazy understanding of what differentiates it from instance_method , as the two do seem to be remarkably similar.

Here's an example of two different ways of defining a class method:

class Example
  class << self
    def class_def
      :class_def
    end
  end

  instance_eval do
    def instance_def
      :instance_def
    end
  end
end

You can check that these work by calling the methods:

puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => :instance_def

The difference is when you're dynamically creating methods using define_method since the binding does appear to be incorrect on the instance_eval version:

class Example
  class << self
    define_method(:class_def) do
      :class_def
    end
  end

  instance_eval do
    define_method(:instance_def) do
      :instance_def
    end
  end
end

This results in the instance_def method being defined, but not being bound to the class itself:

puts Example.class_def.inspect
# => :class_def
puts Example.instance_def.inspect
# => NoMethodError: undefined method ‘instance_def’ for Example:Class

The only reliable way to create dynamic methods is with class << self . The method instance_def appears to be created and discarded as it doesn't show up in Example.methods even inside that block.

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