简体   繁体   English

使用`instance_eval`

[英]Using `instance_eval`

I am reading why's poignant guide to ruby, and in chapter 6, he used this code: 我正在阅读为什么是红宝石的辛辣指南,在第6章中,他使用了以下代码:

class Creature
  # Get a metaclass for this class
  def self.metaclass; class << self; self; end; end

  ...

  def self.traits( *arr )
    # 2. Add a new class method to for each trait.
    arr.each do |a|
      metaclass.instance_eval do
        define_method( a ) do |val|
          @traits ||= {}
          @traits[a] = val
        end
      end
    end
  end
end

Why is he calling instance_eval on metaclass of class Creature ? 为什么他在Creature类的元类上调用instance_eval Since instance_eval adds methods to metaclass , he can just do this: 由于instance_eval将方法添加到metaclass ,他可以这样做:

def self.metaclass; self; end;

Or am I wrong? 还是我错了? Are there any more elegant solutions to this? 还有其他更优雅的解决方案吗?

If you do 如果你这样做

def self.metaclass; self; end;

you will have a reference to Creature class. 您将参考Creature类。 So, in that case, the methods will be defined not to the singleton class of object Creature but to the class Creature itself(to the list of instance methods of class Creature ). 所以,在这种情况下,该方法将被定义为不将单例类对象的Creature ,但对类Creature本身(到类的实例方法列表Creature )。 The method 方法

def self.metaclass; class << self; self; end; end

is a simple way to retrieve singleton class of object Creature in ruby < 1.9. 是在<1.9以下的ruby中检索对象Creature单例类的简单方法。 In ruby 1.9+ was implemented method singleton_class which is shortcut of class << self . 在ruby 1.9+中实现了方法singleton_class ,这是class << self快捷方式。 So that code can be simplified as: 这样该代码可以简化为:

class Creature

  ...

  def self.traits( *arr )
    # 2. Add a new class method to for each trait.
    arr.each do |a|
      singleton_class.instance_eval do
        define_method( a ) do |val|
          @traits ||= {}
          @traits[a] = val
        end
      end
    end
  end
end

The simpler way to write _why's code would be just 编写_why代码的更简单方法是

def self.traits( *arr )
  # 2. Add a new class method to for each trait.
  arr.each do |a|
    metaclass.define_method(a) do |val|
      @traits ||= {}
      @traits[a] = val
    end
  end
end

The only problem here is that you get an error: 唯一的问题是出现错误:

private method `define_method' called for metaclass (NoMethodError)

A private method can only be called with an implicit receiver ie the problem is the explicit metaclass. 只能使用隐式接收方调用私有方法,即问题是显式metaclass. before the method call. 在方法调用之前。 But if we remove that, the implicit receiver ( self ) is Creature ! 但是,如果我们删除它,隐式接收者( self )就是Creature So how do we change self to a different object? 那么我们如何将self变成另一个对象呢? instance_eval : instance_eval

metaclass.instance_eval do
  define_method(a) do |val|
    ...
  end
end

So it's really just a way of bypassing the fact that define_method is private. 因此,这实际上只是绕过define_method是私有的事实的一种方式。 Another way to hack it would be to use send 破解的另一种方法是使用send

metaclass.send(:define_method, a) do |val|
  ...
end

But these days all of that is totally unnecessary; 但是如今,这一切都是完全不必要的。 you are allowed to define methods in the metaclass (AKA singleton class) without hacking around private methods: 您可以在元类(AKA单例类)中定义方法,而不必乱砍私有方法:

def self.traits( *arr )
  # 2. Add a new class method to for each trait.
  arr.each do |a|
    define_singleton_method(a) do |val|
      @traits ||= {}
      @traits[a] = val
    end
  end
end

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

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