2010-03-15 146 views
14

我被卡住了。我试图动态地定义一个类方法,我无法将我的头围绕在ruby元类模型上。考虑以下类:Ruby元类疯狂

class Example 

    def self.meta; (class << self; self; end); end 

    def self.class_instance; self; end 

end 

Example.class_instance.class # => Class 
Example.meta.class   # => Class 

Example.class_instance == Example  # => true 
Example.class_instance == Example.meta # => false 

很显然,这两种方法都返回一个Class实例。但是这两个例子 是不一样的。他们也有不同的祖先:

Example.meta.ancestors   # => [Class, Module, Object, Kernel] 
Example.class_instance.ancestors # => [Example, Object, Kernel] 

在元类和类实例之间做出区别有什么意义?

我想通了,我可以send :define_method元组动态定义一个方法,但如果我尝试将它发送到类实例将无法工作。至少我可以解决我的问题,但我仍然想明白为什么它以这种方式工作。

更新2010年3月15日13:40

有以下的假设是否正确。

  • 如果我有一个调用self.instance_eval并定义方法的实例方法,它只会影响该类的特定实例。
  • 如果我有一个调用self.class.instance_eval(与调用class_eval相同)的实例方法并定义了一个方法,它将影响该特定类的所有实例,从而产生新的实例方法。
  • 如果我有一个调用instance_eval并定义一个方法的类方法,它将为所有实例创建一个新的实例方法。
  • 如果我有一个调用meta/eigen类的instance_eval并定义方法的类方法,它将产生一个类方法。

我认为它开始对我有意义。如果类内部的自我指向特征类,它肯定会限制你的可能性。如果是这样,就不可能在类方法中定义一个实例方法。那是对的吗?

回答

11

动态定义一个单身方法很简单,当你使用instance_eval

Example.instance_eval{ def square(n); n*n; end } 
Example.square(2) #=> 4 
# you can pass instance_eval a string as well. 
Example.instance_eval "def multiply(x,y); x*y; end" 
Example.multiply(3,9) #=> 27 

至于差以上,你是在混淆两件事情:

由您定义的元类,就是所谓的Ruby社区singelton类特征类。该单例类是可以添加类(单例)方法的类。

对于您试图使用class_instance方法定义的类实例,除了类本身,只是尝试将实例方法添加到类Example并检查您定义的class_instance方法是否返回类Example本身通过检查方法的存在:

class Example 
    def self.meta; (class << self; self; end); end 
    def self.class_instance; self; end 
    def hey; puts hey; end 
end 

Example.class_instance.instance_methods(false) #=> ['hey'] 

反正来概括你,当你想添加类的方法,只需将它们添加到元类。至于class_instance方法是无用的,只是将其删除。

无论如何,我建议你阅读this post来掌握Ruby反射系统的一些概念。

UPDATE

我建议你阅读这好贴:Fun with Ruby's instance_eval and class_eval, 不幸的是class_evalinstance_eval是困惑,因为他们以某种方式对他们的命名工作!

Use ClassName.instance_eval to define class methods. 

Use ClassName.class_eval to define instance methods. 

现在回答你的假设:

如果我有一个实例方法, 调用self.instance_eval和定义 方法,它只会影响类的 特定实例。

是:

class Foo 
    def assumption1() 
    self.instance_eval("def test_assumption_1; puts 'works'; end") 
    end 
end 

f1 = Foo.new 
f1.assumption1 
f1.methods(false) #=> ["test_assumption_1"] 
f2 = Foo.new.methods(false) #=> [] 

如果我有 电话self.class.instance_eval(其中 将与调用 class_eval),并定义了一个方法,它 将一个实例方法影响该特定类的所有实例,从而产生新的实例方法 。

在类本身这方面将定义特殊方法(不是实例的)没有instance_eval

class Foo 
    def assumption2() 
    self.class.instance_eval("def test_assumption_2; puts 'works'; end") 
    end 
end 

f3 = Foo.new 
f3.assumption2 
f3.methods(false) #=> [] 
Foo.singleton_methods(false) #=> ["test_assumption_2"] 

对于工作代替instance_eval以上class_eval

如果我有一个要求 instance_eval的和定义的方法它 将产生一个新的实例方法 所有实例的类方法。

都能跟得上:

class Foo 
    instance_eval do 
    def assumption3() 
     puts 'works' 
    end 
    end 
end 

Foo.instance_methods(false) #=> [] 

Foo.singleton_methods(false) #=> ["assumption_3"] 

这将使单方法,而不是实例方法。为了这个工作,用上面的class_eval代替instance_eval

如果我有其中要求所述元/本征类 instance_eval的,并限定了方法将导致 一个类方法的类的方法。

嗯,不会,这会做出如此复杂的东西,因为它会将singleton方法添加到singleton类中,我认为这不会有任何实际用途。

+0

欲了解更多有关_why_实例_eval'内'def'定义的类方法,请参阅这篇文章http://yugui.jp/articles/846 – horseyguy 2010-03-15 12:15:05

+0

非常感谢迄今。我更新我的问题。你介意看看它吗? – t6d 2010-03-15 12:44:15

+0

更新后回答更新中的问题。 – khelll 2010-03-15 13:30:07

5

如果定义在类中的方法,可以在其对象调用。这是一个实例方法

class Example 
end 

Example.send :define_method, :foo do 
    puts "foo" 
end 

Example.new.foo 
#=> "foo" 

如果定义在元类的方法,它可以在的类调​​用。这类似于类方法或其他语言的静态方法的概念。

class Example 
    def self.metaclass 
    class << self 
     self 
    end 
    end 
end 

Example.metaclass.send :define_method, :bar do 
    puts "bar" 
end 

Example.bar 
#=> "bar" 

原因即元类存在,是因为你可以做到这一点的红宝石:

str = "hello" 
class << str 
    def output 
    puts self 
    end 
end 

str.output 
#=> "hello" 

"hi".output 
# NoMethodError 

正如你可以看到,我们定义只适用于的一个实例的方法串。我们定义这个方法的东西叫做元类。在方法查找链中,在搜索对象的类之前首先访问元类。

如果我们Class类型的对象代替String类型的对象,你可以想象为什么这意味着我们只定义在特定类中的方法,而不是所有类。

当前上下文和self之间的差异很微妙,如果您有兴趣,您可以read more