为什么instance_eval()在类上调用时定义了一个类方法?

时间:2009-05-23 02:22:24

标签: ruby class-method instance-method

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在此上下文中定义类方法而不是实例方法?

2 个答案:

答案 0 :(得分:9)

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

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

# 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

的对象的实例方法

答案 1 :(得分:1)

' instance_eval'的目的是扩展对象,但目的是' class_eval'是扩展类。因为类也是对象,所以可以在类上应用instance_eval。

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

能够改变每个对象的行为是至关重要的。它以多种语言存在。

考虑instance_eval首先考虑对象。然后,您将意识到类也是对象 - 其他用于创建新对象的对象,用于保存对常见行为(方法)的描述。您不仅可以使用类的定义,还可以将类分配给变量,将类作为参数传递,对类调用方法,编写类!

我会推荐Yehuda Katz和Yugui写的文章深入研究它: