【发布时间】:2012-02-08 16:38:49
【问题描述】:
我有一个使用ObjectSpace#each_object 迭代的脚本,没有参数。然后它打印每个类存在多少个实例。
我意识到有些类重新定义了#class 实例方法,所以我不得不寻找另一种方法来获取实际的类;假设它存储在变量"klass" 中,klass === object 为真。
在 Ruby 1.8 中我可以做到这一点,假设 Object 没有猴子补丁:
Object.instance_method(:class).bind(object).call
这适用于ActiveSupport::Duration 实例:
# Ruby 1.8
# (tries to trick us)
20.seconds.class
=> Fixnum
# don't try to trick us, we can tell
Object.instance_method(:class).bind(20.seconds).call
=> ActiveSupport::Duration
但是,在 Ruby 1.9 中,这不再有效:
# Ruby 1.9
# we are not smart...
Object.instance_method(:class).bind(20.seconds).call
TypeError: bind argument must be an instance of Object
from (irb):53:in `bind'
from (irb):53
from /Users/user/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'
原来ActiveSupport::Duration 是ActiveSupport::BasicObject 的子类。后者是 Ruby 1.9 中 ::BasicObject 的子类,因此 Object 被排除在继承链之外。这在 Ruby 1.8 中不会也不会发生,因此 ActiveSupport::BasicObject 是 Object 的子类。
我还没有找到任何方法来检测不是Object 实例的Ruby 1.9 对象的实际类。 1.9 中的BasicObject 真的很简单:
BasicObject.instance_methods
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
想法?
更新:
由于 ruby 1.9 已报废,我将接受更改为@indirect 的答案。上面提到的 ruby 1.9 仅出于历史目的,以表明从 1.8 到 1.9 的更改是我的问题的原始原因。
【问题讨论】:
-
我曾经遇到过同样的问题,我放弃了。有一个few approaches,但要么对我不起作用,要么过于打扰。也许您可以重新定义您的问题并直接指出希望您在起源中寻找而不是试图使一种可能的方法起作用。
-
@fguillen 感谢您的链接。关于使用 self.inherited 的帖子看起来很有希望。
-
接受了 Frederick Cheung 的回答。我选择它而不是我的解决方案,因为它可能表现更好。其他人可能有不同的要求或限制 - 只需投票您喜欢的任何一个即可。
-
转而接受 paon 的回答。它不依赖于外部库,唯一的缺点是它在您调用它的每个 BasicObject 上分配特征类。我要做的唯一更改是将方法定义为
__realclass__而不是class。 -
仅供参考:根据 paon 的解决方案查看我的新答案。我保留了对 paon 的接受,因为核心思想是他的。