【问题标题】:Add method to an instanced object向实例对象添加方法
【发布时间】:2010-12-25 15:53:37
【问题描述】:
obj = SomeObject.new

def obj.new_method
  "do some things"
end

puts obj.new_method
> "do some things"

这工作正常。但是,我需要在现有方法中做同样的事情:

def some_random_method
  def obj.new_method
    "do some things"
  end
end

也可以,但是在方法中包含方法看起来很糟糕。问题是,有没有其他方法可以添加这样的方法?

【问题讨论】:

  • 你确定第二个sn-p代码有效吗?因为我认为它应该首先接受obj 作为方法参数来定义一个单例方法。
  • 向下滚动一点。 SO 的算法有时会将更新的答案放在顶部。
  • 有趣。以后我会看得更远。

标签: ruby


【解决方案1】:

在 ruby​​ 1.9+ 中,使用define_singleton_method 有更好的方法,如下所示:

obj = SomeObject.new

obj.define_singleton_method(:new_method) do
  "do some things"
end

【讨论】:

  • 如果我在这个do 块内使用@field 会发生什么?我将访问当前类的字段,还是使用obj 的字段?
  • 还没有真正正确地测试过这个,但据我所知,mixin 和这种方法并不等效。这可以访问外部值,而 mixin 则不能。
【解决方案2】:

使用 Mixin。

module AdditionalMethods
  def new_method
    "do some things"
  end
end

obj = SomeObject.new
obj.extend(AdditionalMethods)

puts obj.new_method
> "do some things"

【讨论】:

    【解决方案3】:

    有几种方法可以实现,都与单例类有关:

    1. 可以使用class <<成语打开单例类定义:

       obj = Object.new
       class << obj
         def my_new_method
            ...
         end
       end
      
    2. 或者你可以在obj上使用define_singleton_method

       obj = Object.new
       obj.define_singleton_method(:my_new_method) do
            ...
       end
      
    3. 您也可以使用单例类中的define_method

       obj = Object.new
       obj.singleton_class.define_method(:my_new_method) do
            ...
       end
      
    4. 或者你可以直接使用def

       obj = Object.new
       def obj.my_new_method
            ...
       end
      

    注意示例 3,我认为单例类的概念在该示例上变得更加清晰。这两个例子是有区别的:

        a = Object.new
        b = Object.new
        
        # -- defining a new method in the object's "class" --
        a.class.define_method(:abc) do
          puts "hello abc"
        end
        
        a.abc # prints "hello abc"
        b.abc # also prints "hello abc"
    
        # -- defining a new method in the object's "singleton class" --
        a.singleton_class.define_method(:bcd) do
          puts "hello bcd"
        end
        
        a.bcd # prints "hello bcd"
        b.bcd # error undefined method
    

    这是因为每个对象都有自己的单例类:

        a = Object.new
        b = Object.new
    
        p a.class # prints "Object"
        p a.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84438>>"
    
        p b.class # also prints "Object"
        p b.singleton_class # prints "#<Class:#<Object:0x000055ebc0b84410>>" (a different reference address)
    

    【讨论】:

    • 您好,有拼写错误。 2的代码中的define_sigleton_method应该是define_singleton_method。
    • @fanjieqi 已修复。谢谢!
    【解决方案4】:

    只是一个有趣的注意点:

    如果你已经走了:

    def my_method
        def my_other_method; end
    end
    

    那么 my_other_method 实际上会在对象的 CLASS 上定义,尽管my_method 的接收者是一个实例。

    但是,如果你去(像你一样):

    def my_method
        def self.my_other_method; end
    end
    

    然后my_other_method定义在实例的特征类上。

    与您的问题没有直接关系,但还是很有趣;)

    【讨论】:

      【解决方案5】:

      您可以使用模块。

      module ObjSingletonMethods
        def new_method
          "do some things"
        end
      end
      
      
      obj.extend ObjSingletonMethods
      
      puts obj.new_method # => do some things
      

      现在,如果您需要向该对象添加更多方法,您只需实现模块中的方法即可。

      【讨论】:

        【解决方案6】:

        使用instance_eval:

        obj = SomeObject.new
        
        obj.instance_eval do
          def new_method
            puts 'do something new'
          end
        end
        
        obj.new_method 
        > "do something new"
        

        【讨论】:

          【解决方案7】:
          class Some
          end 
          
          obj = Some.new
          
          class << obj
            def hello 
              puts 'hello'
            end 
          end 
          
          obj.hello
          
          obj2 = Some.new
          obj2.hello # error
          

          语法class &lt;&lt; obj 意味着我们正在为一个对象打开类的定义。您可能知道我们可以使用如下语法定义 Ruby 类方法:

          class Math
          
              class << self 
          
                  def cos(x)
                      ...
                  end 
          
                  def sin(x)
                      ...
                  end 
              end 
          end 
          

          那么我们可以像这样使用这些方法:

          Math.cos(1)
          

          在 Ruby 中,一切都是对象——甚至是类。 self 这里是Math class 本身的对象(您可以使用Math.class 访问该对象)。所以语法class &lt;&lt; self 意味着我们正在为Math class object 开课。是的,这意味着Math class 也有类(Math.class.class)。

          【讨论】:

            【解决方案8】:

            使用 Mixin 的另一种方式

            obj = SomeObject.new
            class << obj
              include AnotherModule
            end
            

            这包括从AnotherModule 到当前对象的所有方法。

            【讨论】:

              猜你喜欢
              • 2014-12-22
              • 2020-02-20
              • 1970-01-01
              • 2013-09-23
              相关资源
              最近更新 更多