元编程:如何发现对象的真实类?

我在Ruby中使用元编程开玩笑,我做了这段代码:

class Class def ===(other) other.kind_of?(self) end end class FakeClass def initialize(object) methods.each {|m| eval "undef #{m}" if m.to_sym != :methods } define = proc do |m| eval(<<-END) def #{m}(*a, &b) @object.#{m}(*a, &b) rescue Object raise $!.class, $!.message.gsub("FakeClass", @object.class.to_s), $!.backtrace-[$!.backtrace[-caller.size-1]] end END end object.methods.each {|m| define[m] } def method_missing(name, *a, &b) if @object.methods.include?(name.to_s) define[name] eval "@object.#{name}(*a, &b)" elsif @object.methods.include?("method_missing") eval "@object.#{name}(*a, &b)" else super end rescue Object raise $!.class, $!.message.gsub("FakeClass", @object.class.to_s), $!.backtrace-[$!.backtrace[-caller.size-1]] end @object = object end end 

这会创建一个模仿对象的假类。 看:

 a = FakeClass.new(1) # => 1 a.class # => Fixnum a.methods # => Return all Fixnum methods a + 1 # => 2 (is not a FakeClass) Fixnum === a # => true a.something # => NoMethodError: # undefined method `something' for 1:Fixnum class Fixnum def foo true end end a.foo # => true 

问题是,现在我不知道如何知道对象是真实的还是假的。 换句话说,如果#class返回对象的真实类。 存在一些纯粹的ruby方式来区分?

想想在我不知道FakeClass存在的情况下,或者我不知道FakeClass的名称是什么。 这意味着我无法编辑FakeClass来添加像#is_fake?这样的方法#is_fake?

PS:我知道a.instance_eval {self}返回对象(不是假的)。 但它无法检查是否是假的。

这是另一个答案:

 a = FakeClass.new(1) b = 1 da = Marshal.dump(a) db = Marshal.dump(b) puts da == db #=> false 

一个滑稽的答案:改变Fixnum,看看它是否坚持。 我猜这种代理方法非常具体,但它可以完成这项任务。

 class Fixnum def foo return true end end a.foo # NoMethodError 

我应该把它变成一个很好的function,但懒惰:)

您可以使用FakeClass实现#class,并返回FakeClass

如:

 class FakeClass #code def class FakeClass end end 

要查看您要代理的对象是哪个类,只需调用即可

 a.object.class #=> Fixnum 

对于那个 FakeClass,我知道@object,我会尝试:

 a = FakeClass.new(5) class << a def fake? self.instance_variable_defined? :@object end end a.fake? #=> true 

关于一个未知的FakeClass,我将比较实例变量的数量:

 fake = (a.instance_variables.length > 1.instance_variables.length) 

你可以在FakeClass中定义你想要的任何方法并用respond_to检查它吗?

 a = FakeClass.new(1) a.respond_to?( 'is_fake_class?' ) 

例如。

我想知道同样的事情。 如果你认为这只是玩弄,尝试挖掘Rails ActiveRecord关联(具体来说: AssociationProxy ),你会发现这种魔法(可怕的那种!)正在进行中。

这是我遇到的一种方式,至少可以检测到这种情况:

 a = FakeClass.new(5) Fixnum.instance_method(:class).bind(a) 

抛出:

 TypeError: bind argument must be an instance of Fixnum 

我觉得必须有更好的方法。