我有一个脚本迭代使用ObjectSpace#each_object没有args.然后它打印每个类存在多少个实例.
我意识到有些类重新定义了#class实例方法,所以我必须找到另一种方法来获得实际的类; 假设它存储在变量中"klass",并且klass === object是真的.
在Ruby 1.8中我可以做到这一点,假设Object没有monkeypatched:
Object.instance_method(:class).bind(object).call
Run Code Online (Sandbox Code Playgroud)
这适用于以下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
Run Code Online (Sandbox Code Playgroud)
但是,在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>'
Run Code Online (Sandbox Code Playgroud)
事实证明,这是ActiveSupport::Duration子类ActiveSupport::BasicObject.后者::BasicObject在Ruby 1.9中是子类,因此Object从继承链中排除.这不会,也不会发生在Ruby 1.8中,因此ActiveSupport::BasicObject是它的子类Object.
我还没有找到任何方法来检测不是实例的Ruby 1.9对象的实际类Object.BasicObject在1.9中是非常简单的:
BasicObject.instance_methods
=> [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
Run Code Online (Sandbox Code Playgroud)
想法?
更新:
由于红宝石1.9达到寿命终止,我正在改变我对@ indirect的回答.上面提到的红宝石1.9只是出于历史目的,表明从1.8到1.9的变化是我问题的最初原因.
pao*_*aon 13
以下解决方案涉及本征类的超类.因此,它具有副作用分配eigenclass(由可检测的ObjectSpace.count_objects[:T_CLASS]在MRI).但由于BasicObject#class 仅在空白平板对象(即不是类型的对象Object,即非Objects对象)上调用,因此副作用也仅适用于空白平板对象.对于Objects,Kernel#class调用标准.
class BasicObject
def class
(class << self; self end).superclass
end
end
# tests:
puts RUBY_VERSION # 1.9.2
class B < BasicObject; end
class X; end
p BasicObject.new.class # BasicObject
p B .new.class # B
p X .new.class # X
p 6.class # Fixnum
p B.instance_method(:class).owner # BasicObject
p X.instance_method(:class).owner # Kernel
p 6.method(:class).owner # Kernel
Run Code Online (Sandbox Code Playgroud)
编辑 - 注意:确实存在问题ActiveSupport::Duration.此类使用interception(method_missing)将消息重定向到:value属性.因此,它为其实例提供了错误的内省.为了保留这种虚假性,有必要为类映射使用另一个名称,例如建议的__realclass__.因此,修改后的解决方案可能如下所示:
class BasicObject
def __realclass__; (class << self; self end).superclass end
end
class Object; alias __realclass__ class end
Run Code Online (Sandbox Code Playgroud)
不调用的另一种方式class << self对ObjectS是通过Module#===,因为这个页面上所建议的开尔文.
如果您可以升级到Ruby 2.0,则根本不需要实现任何内容:
>> Kernel.instance_method(:class).bind(BasicObject.new).call
=> BasicObject
Run Code Online (Sandbox Code Playgroud)
我不知道在Ruby中这样做,但使用C API到Ruby是很简单的.RubyInline Gem可以很容易地为你的Ruby代码添加C:
require 'inline'
class Example
inline do |builder|
builder.c_raw_singleton <<SRC, :arity => 1
VALUE true_class(VALUE self, VALUE to_test) {
return rb_obj_class(to_test);
}
SRC
end
end
Run Code Online (Sandbox Code Playgroud)
然后:
1.9.2p180 :033 > Example.true_class(20.minutes)
=> ActiveSupport::Duration
Run Code Online (Sandbox Code Playgroud)
fguillen的链接让我想到了这种方式.
优点:
缺点:
.
class BasicObject
def self.inherited(klass)
klass.send(:define_method, :__realclass__) { klass }
end
def __realclass__
BasicObject
end
end
# ensures that every Object will also have this method
class Object
def __realclass__
Object.instance_method(:class).bind(self).call
end
end
require 'active_support/core_ext'
20.seconds.__realclass__ # => ActiveSupport::Duration
# this doesn't raise errors, so it looks like all objects respond to our method
ObjectSpace.each_object{|e| e.__realclass__ }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1605 次 |
| 最近记录: |