gpt4 book ai didi

ruby - 动态创建类方法

转载 作者:数据小太阳 更新时间:2023-10-29 07:53:31 24 4
gpt4 key购买 nike

我正在编写一个类方法来创建另一个类方法。 class_evalinstance_eval 如何在类方法的上下文中运行似乎有些奇怪。举例说明:

class Test1
def self.add_foo
self.class_eval do # does what it says on the tin
define_method :foo do
puts "bar"
end
end
end
end

Test1.add_foo # creates new instance method, like I'd expect
Test1.new.foo # => "bar"


class Test2
def self.add_foo
self.instance_eval do # seems to do the same as "class_eval"
define_method :foo do
puts "bar"
end
end
end
end

Test2.add_foo # what is happening here?!
Test2.foo # => NoMethodError
Test2.new.foo # => "bar"


class Test3
def self.add_foo
(class << self; self; end).instance_eval do # call explicitly on metaclass
define_method :foo do
puts "bar"
end
end
end
end

Test3.add_foo # => creates new class method, as I'd expect
Test3.foo # => "bar"

我的理解是,类方法是在相关类的元类上定义的实例方法(在本例中为 Test2)。基于该逻辑,我希望类方法调用 add_foo 的接收者是元类。

  • Test2.add_foo 方法中,self 指的是什么?
  • 为什么在此接收器对象上调用 instance_eval 会创建一个实例方法?

最佳答案

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实际上是在类主体的上下文中发生的方法调用,您不能从实例中调用它。当您尝试调用 id 时与 class_eval , 它失败了因为 id是在实例上定义的方法,而不是在类中。

同时定义方法 class_evalinstance_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>

所以这里发生的是,在上面的第一个 puts 语句中,我们看到 inspect 告诉我们 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 时在方法的上下文中,您实际上是在调用 instance_eval在类本身上,这意味着您正在 Test4 上创建实例方法。我在特征类中调用 instance_eval 的地方怎么样?它在 Test4 的特征类实例上创建一个方法:

Test4.razors #=> Sounds painful

希望这能解决您的一些问题。我知道我在输入这个答案时学到了一些东西!

关于ruby - 动态创建类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13866047/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com