【问题标题】:Why does instance_eval() define a class method when called on a class?为什么 instance_eval() 在类上调用时会定义类方法?
【发布时间】:2009-05-23 02:22:23
【问题描述】:
Foo = Class.new
Foo.instance_eval do
  def instance_bar
    "instance_bar"
  end
end
puts Foo.instance_bar       #=> "instance_bar"
puts Foo.new.instance_bar   #=> undefined method ‘instance_bar’

我的理解是,在对象上调用instance_eval 应该允许您为该对象定义实例变量或方法。

但是在上面的例子中,当你在类 Foo 上调用它来定义 instance_bar 方法时,instance_bar 变成了一个类方法,可以用“Foo.instance_bar”调用。很明显,这段代码没有创建实例方法,因为 Foo.new.instance_bar 导致“未定义的方法‘instance_bar’”。

为什么在这种情况下 instance_eval 定义的是类方法而不是实例方法?

【问题讨论】:

    标签: ruby class-method instance-method


    【解决方案1】:

    x.instance_eval 更改您的上下文,因此 self 的计算结果为 x

    这允许您做很多事情,包括定义实例变量和实例方法,但仅限于 x。

     x = Object.new
     y = Object.new
    
     # define instance variables for x and y
     x.instance_eval { @var = 1 }
     y.instance_eval { @var = 2 }
    
     # define an instance method for all Objects
     class Object
       def var
         @var
       end
     end
    
     x.var #=> 1
     y.var #=> 2
    

    Ruby 允许您在几个地方为对象定义实例方法。一般, 在一个类中定义它们,并且这些实例方法在所有实例之间共享 该类的(如上面的def var)。

    但是,我们也可以为单个对象定义一个实例方法:

    # here's one way to do it
    def x.foo
      "foo!"
    end
    # here's another
    x.instance_eval do
      # remember, in here self is x, so bar is attached to x.
      def bar
        "bar!"
      end
    end
    

    即使xy 具有相同的类,它们也不共享这些方法,因为它们只是为x 定义的。

    x.foo #=> "foo!"
    x.bar #=> "bar!"
    y.foo #=> raises NoMethodError
    y.bar #=> raises NoMethodError
    

    现在在 ruby​​ 中,一切都是对象,甚至是类。类方法只是实例方法 对于那个类对象。

    # we have two ways of creating a class:
    class A 
    end
    # the former is just syntatic sugar for the latter
    B = Class.new
    
    # we have do ways of defining class methods:
    
    # the first two are the same as for any other object
    def A.baz
      "baz!"
    end
    A.instance_eval do
       def frog
         "frog!"
       end
    end
    
    # the others are in the class context, which is slightly different
    class A
      def self.marco
        "polo!"
      end
      # since A == self in here, this is the same as the last one.
      def A.red_light
        "green light!"
      end
    
      # unlike instance_eval, class context is special in that methods that
      # aren't attached to a specific object are taken as instance methods for instances
      # of the class
      def example
         "I'm an instance of A, not A itself"
      end
    end
    # class_eval opens up the class context in the same way
    A.class_eval do
      def self.telegram
        "not a land shark"
      end
    end
    

    再次注意,所有这些方法都是A 特定的,B 无法访问其中任何一个:

    A.baz #=> "baz!"
    B.telegram #=> raises NoMethodError
    

    要从这里带走的重要一点是 类方法只是类Class的对象的实例方法

    【讨论】:

    • 当你写“A.instance_eval do def A.frog 'frog!'结束结束”你说“A.frog”有意义吗?或者你也可以在那里说“self.frog”?稍后,当您定义“A.class_eval do def self.telegram 'not a land shark' end end”时,您会说“self.telegram”而不是“A.telegram” - 同样,这些是可互换的还是重要的是你用一个。”例如 instance_eval 和“self”。对于 class_eval?
    • 您说“要从这里带走的重要一点是,类方法只是类 Class 对象的实例方法”。所以这里的类 A 和 B 实际上是实例,甚至在我们实例化它们之前(类的实例)?我认为我们必须在获得实例之前调用 new() 方法。
    • 嘿,我搞砸了一点。 A.frog 中的 A. 不是必需的(我已经修复了它) - 所以请查看 x 和 A 之间的并行性。
    • 在类上下文块(类 X...end 或 X.class_eval {...})中 def self.foo 和 def X.foo 是等价的,因为 self == X 在那个上下文。
    • A 和 B 是类 Class 的实例。在这里,class A ; end 产生与A = Class.new 相同的结果。
    【解决方案2】:

    “instance_eval”的目的是扩展对象,而“class_eval”的目的是扩展类。而且因为类也是对象,所以您可以在类上应用 instance_eval。

    我猜想在经典 OOP 中类的扩展更容易理解。动态语言允许我们轻松地指定特定对象的行为。事实上,每个对象都可以有自己的行为,这为设计应用程序增加了很多灵活性。对于同一类的对象,不仅数据会有所不同。两个人之所以不同,不仅因为他们出生在不同的年份,不仅因为他们有不同的父母,而且他们的思维方式不同,因此行为也不同。

    改变每个对象的行为的能力是基本的。它以多种语言存在。

    考虑 instance_eval 首先考虑对象。然后你会意识到类也是对象——对象的附加目的是创建新对象,保存对常见行为(方法)的描述。您不仅可以使用类的定义,还可以将类分配给变量,将类作为参数传递,在类上调用方法,对类进行编程!

    我会推荐 Yehuda Katz 和 Yugui 撰写的文章来深入了解它:

    【讨论】:

      猜你喜欢
      • 2013-07-17
      • 2017-10-31
      • 2010-09-20
      • 2021-08-24
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      • 2016-01-27
      • 2011-01-18
      相关资源
      最近更新 更多