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在此上下文中定义类方法而不是实例方法?
答案 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
即使x
和y
具有相同的类,它们也不会共享这些方法,因为它们仅为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写的文章深入研究它: