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]
区分元类和类实例有什么意义?
我发现,我可以发送 :define_method
到元类来动态定义一个方法,但如果我尝试将它发送到类实例,它将不起作用。至少我可以解决我的问题,但我仍然想了解为什么它会这样工作。
更新于 2010 年 3 月 15 日 13:40
以下假设是否正确?
- 如果我有一个调用 self.instance_eval 并定义方法的实例方法,它只会影响该类的特定实例。
- 如果我有一个实例方法,它调用 self.class.instance_eval (这与调用 class_eval 相同)并定义一个方法,它将影响该特定类的所有实例,从而产生一个新的实例方法。
- 如果我有一个类方法调用instance_eval并定义一个方法,它将为所有实例生成一个新的实例方法。
- 如果我有一个类方法,它在元/特征类上调用instance_eval并定义一个方法,它将产生一个类方法。
我想这对我来说开始有意义了。如果类方法中的 self 指向本征类,那么它肯定会限制您的可能性。如果是这样,就不可能从类方法内部定义实例方法。这是正确的吗?
I'm stuck. I'm trying to dynamically define a class method and I can't wrap my head around the ruby metaclass model. Consider the following class:
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
Obviously both methods return an instance of Class. But these two instances
are not the same. They also have different ancestors:
Example.meta.ancestors # => [Class, Module, Object, Kernel]
Example.class_instance.ancestors # => [Example, Object, Kernel]
What's the point in making a difference between the metaclass and the class instance?
I figured out, that I can send :define_method
to the metaclass to dynamically define a method, but if I try to send it to the class instance it won't work. At least I could solve my problem, but I still want to understand why it is working this way.
Update Mar 15, 2010 13:40
Are the following assumptions correct.
- If I have an instance method which calls self.instance_eval and defines a method, it will only affect the particular instance of that class.
- If I have an instance method which calls self.class.instance_eval (which would be the same as calling class_eval) and defines a method it will affect all instances of that particular class resulting in a new instance method.
- If I have a class method which calls instance_eval and defines a method it will result in a new instance method for all instances.
- If I have a class method which calls instance_eval on the meta/eigen class and defines a method it will result in a class method.
I think it starts to make sense to me. It would certainly limit your possibilities if self inside an class method would point to the eigen class. If so it would not be possible to define an instance method from inside a class method. Is that correct?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用
instance_eval
时,动态定义单例方法很简单:至于上面的区别,您会混淆两件事:
您定义的元类,在 Ruby 社区中称为 singelton 类或特征类。该单例类是您可以添加类(单例)方法的类。
至于你试图使用
class_instance
方法定义的类实例,只不过是类本身,为了证明这一点,只需尝试向类添加一个实例方法Example
并通过检查该方法的存在来检查您定义的class_instance
方法是否返回类Example
本身:无论如何,当您想要添加类方法时,为您求和,只需将它们添加到该元类中即可。至于
class_instance
方法没用,去掉即可。不管怎样,我建议你阅读这篇文章来掌握Ruby反射系统的一些概念。
更新
我建议您阅读这篇好文章:Ruby 的 instance_eval 和 class_eval 的乐趣,
不幸的是,
class_eval
和instance_eval
令人困惑,因为它们以某种方式违背了它们的命名!现在回答你的假设:
是的:
在该上下文中,没有
instance_eval
会在类本身上定义单例方法(不是实例方法):为此,请用上面的
class_eval
替换instance_eval
。不:
这将创建单例方法,而不是实例方法。为此,请将上面的
instance_eval
替换为class_eval
。好吧,不,这会产生如此复杂的东西,因为它将向单例类添加单例方法,我认为这不会有任何实际用途。
Defining a singleton method dynamically is simple when you use
instance_eval
:As for the difference above, you are confusing 2 things:
The meta class defined by you, is what called in Ruby community as singelton class or eigen class. That singleton class is the class that you can add class(singleton) methods to.
As for the class instance you are trying to define using the
class_instance
method, is nothing but the class itself, to prove it, just try adding an instance method to the classExample
and check if theclass_instance
method defined by you returns the classExample
itself by checking the existence of that method:Anyway to sum it for you, when you want to add class methods, just add them to that meta class. As for the
class_instance
method is useless, just remove it.Anyway I suggest you read this post to grasp some concepts of Ruby reflection system.
UPDATE
I suggest you read this nice post: Fun with Ruby's instance_eval and class_eval,
Unfortunately
class_eval
andinstance_eval
are confusing because they somehow work against their naming!Now answering your assumptions:
yes:
no
instance_eval
in that context will define singleton methods(not instance ones) on the class itself:For that to work replace
instance_eval
withclass_eval
above.Nope:
That will make singleton methods, not instance methods. For that to work replace
instance_eval
withclass_eval
above.well no, that will make so sophisticated stuff, as it will add singleton method to the singleton class, I don't think that will have any practical use.
如果您在类上定义方法,则可以在其对象上调用该方法。它是一个实例方法。
如果您在元类上定义方法,则可以在类上调用该方法。这类似于其他语言中的类方法或静态方法的概念。
元类存在的原因是因为您可以在 Ruby 中执行此操作:
如您所见,我们定义了一个仅可用于 String 的一个实例的方法。我们定义此方法的对象称为元类。在方法查找链中,在搜索对象的类之前首先访问元类。
如果我们将
String
类型的对象替换为Class
类型的对象,您可以想象为什么这意味着我们只在特定的上定义一个方法 em> 类,并非所有类。当前上下文和
self
之间的差异很微妙,您可以 如果您有兴趣,请阅读更多内容。If you define a method on a class, it can be invoked on its objects. It is an instance method.
If you define a method on a metaclass, it can be invoked on the class. This is similar to the concept of a class method or static method in other languages.
The reason that metaclasses exist is because you can do this in Ruby:
As you can see, we defined a method that is only available to one instance of a String. The thing that we defined this method on is called the metaclass. In the method lookup chain, the metaclass is accessed first before searching the object's class.
If we replace the object of type
String
with an object of typeClass
, you can imagine why this means we're only defining a method on a specific class, not on all classes.The differences between the current context and
self
are subtle, you can read more if you're interested.