开发者

Instance_eval: why the class of subclass is superclass

开发者 https://www.devze.com 2022-12-28 18:53 出处:网络
def singleton_class class << self self end end class Human proc = lambda { puts \'proc says my class is \' + self.name.to_s }
    def singleton_class
       class << self
         self
       end
    end

    class Human
      proc = lambda { puts 'proc says my class is ' + self.name.to_s }

      singleton_class.instance_eval do
        define_method(:lab)  do 
          proc.call
       开发者_Go百科 end
      end
    end

    class Developer < Human
    end

    Human.lab # class is Human
    Developer.lab # class is Human ; oops

Following solution works.

def singleton_class
  class << self
    self
  end
end

class Human
  proc =  lambda { puts 'proc says my class is ' + self.name.to_s }
  singleton_class.instance_eval do
    define_method(:lab) do
      self.instance_eval &proc
    end
  end
end

class Developer < Human
end

Human.lab # class is Human
Developer.lab # class is Human ; oops

Why Developer.lab is reporting that it is Human ? And what can be done so that proc reports Developer when Developer.lab is invoked.


It's subtle, but it boils down to simply calling the block (in which case it acts as a normal closure, and self corresponds to where it was defined, i.e. in Human), or using it (directly) as a block for a method definition or instance_eval:

def singleton_class
   class << self
     self
   end
end


class Human

  PROC = proc { puts 'proc says my class is ' + self.name.to_s }

  singleton_class.instance_eval do
    define_method(:lab)  do 
      PROC.call
    end
    define_method(:lab2, &PROC.method(:call))

    define_method(:lab3)  do 
      instance_eval(&PROC)
    end
    define_method(:lab4, &PROC) 
  end
end

class Developer < Human
end

Human::PROC.call  # => "class is Human"  (original closure)
Developer.lab     # Same as previous line, so "class is Human"  (original closure)
Developer.lab2    # ditto

Developer.instance_eval(&Human::PROC)  # => "class is Developer"  (instance_eval changes sets a different scope)
Developer.lab3    # Same as previous line, so "class is Developer"
Developer.lab4    # ditto


The closure is capturing self in the context where it is defined - just like closures should do. So, when it is called, it will use the references to the context it captured. A closure is not the ideal tool to define the intended functionality. Instead of proc.call, the content of the "define_method" invocation should be "puts 'proc says my class is ' + name.to_s"


I have to think a little bit about exactly why this works, but for the moment it does work:

class Human
  proc = -> { name }

  define_singleton_method(:lab, &proc)
end

class Developer < Human; end

require 'test/unit'
class TestClosures < Test::Unit::TestCase
  def test_that_the_human_class_is_named_human
    assert_equal 'Human', Human.lab
  end
  def test_that_the_developer_class_is_named_developer
    assert_equal 'Developer', Developer.lab
  end
end
0

精彩评论

暂无评论...
验证码 换一张
取 消