instance_eval
和class_eval
之間的主要區別在於一個實例的上下文中instance_eval
作品,而類的上下文內class_eval
作品。我不知道你是多麼熟悉Rails的,但讓我們來看看本作的例子:
class Test3 < ActiveRecord::Base
end
t = Test3.first
t.class_eval { belongs_to :test_25 } #=> Defines a relationship to test_25 for this instance
t.test_25 #=> Method is defined (but fails because of how belongs_to works)
t2 = Test3.find(2)
t2.test_25 #=> NoMethodError
t.class.class_eval { belongs_to :another_test }
t.another_test #=> returns an instance of another_test (assuming relationship exists)
t2.another_test #=> same as t.another_test
t.class_eval { id } #=> NameError
t.instance_eval { id } #=> returns the id of the instance
t.instance_eval { belongs_to :your_mom } #=> NoMethodError
這是因爲belongs_to
實際上是一個方法調用類體,它不能調用的範圍內發生的是從一個實例。當您嘗試使用class_eval
調用id
時,它會失敗,因爲id
是在實例上定義的方法,而不是在類中定義的方法。
定義使用class_eval
和instance_eval
的方法在針對實例調用時基本相同。他們只會在被調用的對象實例上定義一個方法。
t.class_eval do
def some_method
puts "Hi!"
end
end
t.instance_eval do
def another_method
puts "Hello!"
end
end
t.some_method #=> "Hi!"
t.another_method #=> "Hello!"
t2.some_method #=> NoMethodError
t2.another_method #=> NoMethodError
但是,它們在處理班級時有所不同。
t.class.class_eval do
def meow
puts "meow!"
end
end
t.class.instance_eval do
def bark
puts "woof!"
end
end
t.meow #=> meow!
t2.meow #=> meow!
t.bark #=> NoMethodError
t2.bark #=> NoMethodError
那麼樹皮去哪了?它定義在類的單例類的實例上。我會在下面解釋更多。但現在:
t.class.bark #=> woof!
Test3.bark #=> woof!
所以回答你什麼self
是指類體中的問題,你可以構造一個小測試:
a = class Test4
def bar
puts "Now, I'm a #{self.inspect}"
end
def self.baz
puts "I'm a #{self.inspect}"
end
class << self
def foo
puts "I'm a #{self.inspect}"
end
def self.huh?
puts "Hmmm? indeed"
end
instance_eval do
define_method :razors do
puts "Sounds painful"
end
end
"But check this out, I'm a #{self.inspect}"
end
end
puts Test4.foo #=> "I'm a Test4"
puts Test4.baz #=> "I'm a Test4"
puts Test4.new.bar #=> Now I'm a #<Test4:0x007fa473358cd8>
puts a #=> But check this out, I'm a #<Class:Test4>
那麼,什麼是這裏發生的事情是,在首先在上面聲明,我們看到檢查告訴我們,在類方法體的上下文中的self
指的是類Test4。在第二個puts
中,我們看到了相同的事物,它的定義有所不同(使用self.method_name
表示法來定義類方法)。第三,我們看到self
引用了Test4的一個實例。最後一個有點有趣,因爲我們看到的是self
指的是Class
的一個實例,名爲Test4
。這是因爲當你定義一個類時,你正在創建一個對象。 Ruby中的所有東西都是一個對象。這個對象的實例被稱爲元類或特徵類或單例類。
您可以使用class << self
慣用語訪問本徵類。當你在那裏時,你實際上可以訪問特徵類的內部。您可以在本徵類內定義實例方法,這與調用self.method_name
一致。但是由於您處於特徵類的範圍內,因此您可以將方法附加到特徵類的特徵類。
Test4.huh? #=> NoMethodError
Test4.singleton_class.huh? #=> Hmmm? indeed
當你在方法的上下文中調用instance_eval
,你真的類本身,這意味着你正在創建TEST4實例方法調用instance_eval
。那麼我在本徵類內部稱爲instance_eval的地方呢?它在Test4的本徵類的實例上創建一個方法:
Test4.razors #=> Sounds painful
希望這可以清除您的一些問題。我知道我已經學會了一些輸入這個答案的東西!
instance_eval在這裏解釋:http://stackoverflow.com/questions/13775794/a-confusing-case-in-ruby-metaprogramming/13790992#13790992 – BernardK
有趣的:http://stackoverflow.com/questions/14428531/ ruby-instance-eval-on-a-class-with-attr-accessor/14431718#14431718和鏈接的文章。 – BernardK