6
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 在这种情况下定义类方法而不是实例方法?

4

2 回答 2

10

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

于 2009-05-23T03:00:32.113 回答
2

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

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

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

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

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

于 2014-04-29T07:36:09.267 回答