简体   繁体   English

instance_eval vs模块中的class_eval

[英]instance_eval vs class_eval in module

class Foo
    include Module.new { class_eval "def lab; puts 'm' end" }

    def lab
      super 
      puts 'c'
    end
end

Foo.new.lab #=> m c

======================================================================== ================================================== ======================

class Foo
    include Module.new { instance_eval "def lab; puts 'm' end" }

    def lab
      super 
      puts 'c'
    end
end

Notice here I changed class_eval to instance_eval 请注意,我将class_eval更改为instance_eval

Foo.new.lab rescue nil#=> no super class method lab
Foo.lab #=> undefined method lab for Foo class

So it seems that including the module neither defined an instance method nor a class method. 所以似乎包含模块既没有定义实例方法也没有定义类方法。

Any explanation what's going on here? 有什么解释在这里发生了什么?

This code was tested on ruby 1.8.7 on mac. 此代码在mac上的ruby 1.8.7上进行了测试。

First, think of what include does. 首先,想到什么include呢。 it makes the instance methods of the module being included into instance methods on the including class. 它使模块的实例方法被包含在包含类的实例方法中。 ie apart from the fact that your working example uses an anonymous module it is equivalent to: 即,除了您的工作示例使用匿名模块这相当于:

module M1
  def lab
    puts 'm'
  end
end

class Foo
    include M1

    def lab
      super 
      puts 'c'
    end
end

Next, think of what class_eval does. 接下来,想一想class_eval作用。 It evaluates the given code in the context of the class or module. 它在类或模块的上下文中评估给定的代码。 ie it's exactly like you reopened the module and typed the code passed to class_eval . 即它就像你重新打开模块并输入传递给class_eval的代码class_eval So MyModule = Module.new { class_eval "def lab; puts 'm' end" } is equivalent to 所以MyModule = Module.new { class_eval "def lab; puts 'm' end" }相当于

module MyModule
  def lab
    puts 'm'
  end
end

Hopefully this explains the case that works. 希望这能解释有效的案例。

When you use instance_eval you are evaluating the code within the context of the receiving object (in this case the instance of module) so MyMod2 = Module.new { instance_eval "def lab; puts 'm' end" } is equivalent to 当你使用instance_eval你正在评估接收对象的上下文中的代码(在这种情况下是模块的实例)所以MyMod2 = Module.new { instance_eval "def lab; puts 'm' end" }相当于

module MyMod2
  def MyMod2.lab
    puts 'm'
  end
end

ie it creates a module method which you'd call via MyMod2.lab and such methods are not added as instance methods by include . 即它创建了一个模块方法,您可以通过MyMod2.lab调用MyMod2.lab并且这些方法不会被include作为实例方法include


Please note : this answer borrows a bit of its explanation from an answer I wrote to a previous question asking about instance_eval vs. class_eval relating to an example from The Ruby Programming Language book. 请注意 :这个答案从我写给前一个问题的答案中借用了一些解释,这个问题询问了与 Ruby编程语言书中的一个例子有关的instance_eval与class_eval You might find that answer helpful too. 您可能会发现答案也很有用。

including a module just takes the instance methods - you are looking for extend. 包括一个模块只需要实例方法 - 你正在寻找扩展。 luckily to get the best of both worlds, you can simply do: 幸运的是,为了获得两全其美,你可以简单地做到:

module Something
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def blah
      puts "lol"
    end
  end
end

class Test
  include Something
end 

irb: IRB:

>> Test.blah
lol
=> nil

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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