可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Foo = Class.new
Foo.class_eval do
def class_bar
"class_bar"
end
end
Foo.instance_eval do
def instance_bar
"instance_bar"
end
end
Foo.class_bar #=> undefined method ‘class_bar’ for Foo:Class
Foo.new.class_bar #=> "class_bar"
Foo.instance_bar #=> "instance_bar"
Foo.new.instance_bar #=> undefined method ‘instance_bar’ for #<Foo:0x7dce8>
Just based on the name of the methods, I would expect class_eval to allow you to add a class method to Foo and instance_eval to allow you to add an instance method to Foo. But they seem to do the opposite.
In the example above if you call class_bar on the Foo class you get an undefined method error and if you call instance_bar on the instance returned by Foo.new you also get an undefined method error. Both errors seem to contradict an intuitive understanding of what class_eval and instance_eval should do.
What is really the difference between these methods?
Documentation for class_eval:
mod.class_eval(string [, filename [,
lineno]]) => obj
Evaluates the string or block in the
context of mod. This can be used to
add methods to a class.
Documentation for instance_eval:
obj.instance_eval {| | block } => obj
Evaluates a string containing Ruby
source code, or the given block,
within the context of the receiver
(obj). In order to set the context,
the variable self is set to obj while
the code is executing, giving the code
access to obj’s instance variables.
回答1:
As the documentation says, class_eval
evaluates the string or block in the context of the Module or Class. So the following pieces of code are equivalent:
class String
def lowercase
self.downcase
end
end
String.class_eval do
def lowercase
self.downcase
end
end
In each case, the String class has been reopened and a new method defined. That method is available across all instances of the class, so:
"This Is Confusing".lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"
class_eval
has a number of advantages over simply reopening the class. Firstly, you can easily call it on a variable, and it's clear what your intent is. Another advantage is that it will fail if the class doesn't exist. So the example below will fail as Array
is spelt incorrectly. If the class was simply reopened, it would succeed (and a new incorrect Aray
class would be defined):
Aray.class_eval do
include MyAmazingArrayExtensions
end
Finally class_eval
can take a string, which can be useful if you're doing something a little more nefarious...
instance_eval
on the other hand evaluates code against a single object instance:
confusing = "This Is Confusing"
confusing.instance_eval do
def lowercase
self.downcase
end
end
confusing.lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String
So with instance_eval
, the method is only defined for that single instance of a string.
So why does instance_eval
on a Class
define class methods?
Just as "This Is Confusing"
and "The Smiths on Charlie's Bus"
are both String
instances, Array
, String
, Hash
and all other classes are themselves instances of Class
. You can check this by calling #class
on them:
"This Is Confusing".class
=> String
String.class
=> Class
So when we call instance_eval
it does the same on a class as it would on any other object. If we use instance_eval
to define a method on a class, it will define a method for just that instance of class, not all classes. We might call that method a class method, but it is just an instance method for that particular class.
回答2:
The other answer is correct, but allow me to go in depth a little.
Ruby has a number of different kinds of scope; six according to wikipedia, though detailed formal documentation seems to be lacking. The kinds of scope involved in this question are, not surprisingly, instance and class.
The current instance scope is defined by the value of self
. All unqualified method calls are dispatched to the current instance, as are any references to instance variables (which look like @this
).
However, def
is not a method call. The target for methods created by def
is the current class (or module), which can be found with Module.nesting[0]
.
Let's see how the two different eval flavors affect these scopes:
String.class_eval { [self, Module.nesting[0]] }
=> [String, String]
String.instance_eval { [self, Module.nesting[0]] }
=> [String, #<Class:String>]
In both cases, the instance scope is the object on which *_eval is called.
For class_eval
, the class scope also becomes the target object, so def
creates instance methods for that class/module.
For instance_eval
, the class scope becomes the singleton class (aka metaclass, eigenclass) of the target object. Instance methods created on the singleton class for an object become singleton methods for that object. Singleton methods for a class or module are what are commonly (and somewhat inaccurately) called class methods.
The class scope is also used to resolve constants. Class variables (@@these @@things
) are resolved with class scope, but they skip over singleton classes when searching the module nesting chain. The only way I have found to access class variables in singleton classes is with class_variable_get/set
.
回答3:
I think you got it wrong. class_eval adds the method in the class, so all instances will have the method. instance_eval will add the method just to one specific object.
foo = Foo.new
foo.instance_eval do
def instance_bar
"instance_bar"
end
end
foo.instance_bar #=> "instance_bar"
baz = Foo.new
baz.instance_bar #=> undefined method
回答4:
instance_eval effectively creates a singleton method for the object instance in question. class_eval will create a normal method in the given class's context, available to all objects of that class.
Here's a link regarding singleton methods and the singleton pattern(non-ruby specific)
回答5:
instance_eval
and class_eval
allow you to execute a chunk of code. So what you might say? The old-fashioned eval
can do this. But instance_eval
and class_eval
accept a block argument for the chunk of code. So the chunk of code does not need to be a string. Also instance_eval
and class_eval
allow a receiver (unlike the old eval
). Consequently, you can invoke these two modern methods on a class object or even on an instance object.
class A
end
A.instance_eval do
# self refers to the A class object
self
end
a = A.new
a.instance_eval do
# self refers to the a instance object
self
end
Also remember in ruby if we call a method without a receiver, then the method would be invoked on self
, which in the instance_eval
block is the object we invoked instance_eval
on. instance variables are private in ruby. You cannot access them outside the class they are defined in. But since instance variables are stored in self
, we can access them in instance_eval
(the same applies for private methods which cannot be invoked with a receiver):
class A
def initialzie
@a = “a”
end
private
def private_a
puts “private a”
end
end
a = A.new
puts a.instance_eval { @a }
# => “a”
puts a.instance_eval { private_a }
# => “private a”
We can also add methods to the receiver in instance_eval
and class_eval
. Here we add it to instance_eval
:
class A
end
A.instance_eval do
def a_method
puts “a method”
end
end
A.a_method
# => a method
Now think what we just did for a moment. We used instance_eval
, defined a method in its block
, and then invoked the method on the class object itself. Isn’t that a class method? Think of it as a “class” method if you want. But all we did was define a method on the receiver in the instance_eval
block, and the receiver happened to be A
. We can easily do the same on a instance object:
a.instance_eval do
def a_method
puts "a method"
end
end
a.a_method
# => a method
And it works just the same. Don’t think of class methods as class methods in other languages. They are just methods defined on self
, when self
happens to be a class object (extending from Class.new
as in class A end
).
But I want to take this answer a little bit more deeper than the accepted answer. Where does instance_eval
actually stick the methods you place into them? They go into the singleton
class of the receiver! As soon as you invoke instance_eval
on the receiver, the ruby interpreter opens the singleton_class
and places the methods defined in the block inside this singleton_class
. It is just like using extend
in a class (because extend opens up the singleton class and places the methods in the module passed to extend into the singleton class)! It opens up the singleton_class
, which is part of the inheritance hierarchy (right before the parent class): A -> singleton_class -> Parent
Now what makes class_eval
different? class_eval
can only be called on classes and modules. self
still refers to the receiver:
class A
end
A.class_eval do
# self is A
self
end
But unlike instance_eval
, when you define methods in the class_eval
block, they will be available on instances of the class not the class object itself. With class_eval
, the methods are not added to the singleton class in the inheritance hierarchy. Instead the methods are added to the current class
of the receiver! So when you define a method in class_eval
, that method goes directly into the current class
and, thus, it becomes an instance method. So you cannot call it on the class object; you can only call it on instances of the class object.