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

时间:2011-03-27 00:20:36

标签: ruby metaprogramming

我在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返回对象的真实类。存在一些纯粹的红宝石方式来区分?

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

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

6 个答案:

答案 0 :(得分:4)

这是另一个答案:

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

答案 1 :(得分:1)

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

class Fixnum
  def foo
    return true
  end
end

a.foo # NoMethodError

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

答案 2 :(得分:0)

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

如:

class FakeClass
  #code
  def class
    FakeClass
  end
end

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

即可
a.object.class #=> Fixnum

答案 3 :(得分:0)

对于那个 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)

答案 4 :(得分:0)

您可以在FakeClass中定义所需的任何方法,并使用respond_to?

进行检查
a = FakeClass.new(1)

a.respond_to?( 'is_fake_class?' )
例如

答案 5 :(得分:0)

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

这是我跑过来至少检测到这种事情的一种方式:

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

抛出:

TypeError: bind argument must be an instance of Fixnum

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