有没有一种方法可以从Ruby类的实例内部为该实例创建方法?

有没有一种方法可以从Ruby类的实例内部为该实例创建方法?,ruby,Ruby,让类示例定义为: class Example def initialize(test='hey') self.class.send(:define_method, :say_hello, lambda { test }) end end 调用Example.new时;示例.new我得到一个警告:方法被重新定义;丢弃旧的问候语。我总结说,这一定是因为它在实际类中定义了一个方法(从语法上讲,这是有意义的)。当然,如果有多个Example实例在其方法中具有不同的值,那么这将是灾难性的

类示例定义为:

class Example
  def initialize(test='hey')
    self.class.send(:define_method, :say_hello, lambda { test })
  end
end
调用
Example.new时;示例.new
我得到一个
警告:方法被重新定义;丢弃旧的问候语
。我总结说,这一定是因为它在实际类中定义了一个方法(从语法上讲,这是有意义的)。当然,如果有多个
Example
实例在其方法中具有不同的值,那么这将是灾难性的


是否有一种方法可以仅从实例内部为类的实例创建方法?

您需要获取对实例的singleton类的引用,该类包含所有特定于实例的内容,并在其上定义方法。在Ruby1.8中,它看起来有点凌乱。(如果您找到更清洁的解决方案,请告诉我!)

Ruby 1.8

class Example
  def initialize(test='hey')
    singleton = class << self; self end
    singleton.send :define_method, :say_hello, lambda { test }
  end
end

首先,一个小的风格提示:

self.class.send(:define_method, :say_hello, lambda { test })
通过在Ruby 1.9中使用新的proc-literal,您可以使它看起来更好一些:

self.class.send(:define_method, :say_hello, -> { test })
但你不需要这样。Ruby有一种称为块的东西,它基本上是一段代码,可以作为参数传递给方法。实际上,您已经使用了块,因为
lambda
只是一个将块作为参数并返回
Proc
的方法。但是,
define\u方法
已经获取了一个块,不需要将块传递给
lambda
,后者将其转换为
Proc
,然后传递给
define\u方法
,后者将其转换回块:

self.class.send(:define_method, :say_hello) { test }
正如您已经注意到的,您在错误的类上定义了方法。您在
示例
类上定义它,因为在
初始化
这样的实例方法中,
self
是当前对象(即@mikej示例中的
ex1
ex2
),这意味着
self.class
ex1
的类,即
示例
。因此,您正在一次又一次地覆盖相同的方法

这会导致以下不必要的行为:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2') # warning: method redefined; discarding old say_hello
ex1.say_hello            # => ex2 # Huh?!?
相反,如果需要单例方法,则需要在单例类上定义它:

(class << self; self end).send(:define_method, :say_hello) { test }
在Ruby 1.9中,有一种方法可以做到这一点:

define_singleton_method(:say_hello) { test }
现在,它按照您希望的方式工作,但是这里有一个更高级别的问题:这不是Ruby代码。这是Ruby语法,但不是Ruby代码,而是Scheme

现在,Scheme是一种出色的语言,用Ruby语法编写Scheme代码肯定不是一件坏事。用Ruby语法编写Java或PHP代码,或者像昨天在StackOverflow问题中所遇到的那样,用Ruby语法编写Fortran-57代码,这比编写Java或PHP代码要好得多。但它不如用Ruby语法编写Ruby代码好

Scheme是一种函数式语言。函数式语言使用函数(更准确地说,函数闭包)进行封装和状态。但是Ruby不是一种函数式语言,它是一种面向对象的语言,OO语言使用对象进行封装和状态转换

因此,函数闭包成为对象,捕获的变量成为实例变量

我们也可以从一个完全不同的角度来看这个问题:你所做的是定义一个单例方法,这个方法的目的是定义特定于一个对象的行为。但是您为类的每个实例定义了该单例方法,并且为类的每个实例定义了相同的单例方法。我们已经有了一种为类的每个实例定义行为的机制:实例方法

这两个论点来自完全相反的方向,但它们到达了相同的目的地:

class Example
  def initialize(test='hey')
    @test = test
  end

  def say_hello
    @test
  end
end

我知道两年前有人问过这个问题,但我想再补充一个答案
.instance\u eval
将有助于向实例对象添加方法

    string = "String"
    string.instance_eval do
      def new_method
        self.reverse
      end
    end

我冒昧地编辑了答案,使用了术语
singleton class
,因为它是官方名称。实际上,Ruby 1.9.2引入了
Object#singleton_类
@Marc:Oh nice。还没有太多时间玩1.9.2。我很高兴有人最终确定了一个名字。
class Example
  def initialize(test='hey')
    @test = test
  end

  def say_hello
    @test
  end
end
    string = "String"
    string.instance_eval do
      def new_method
        self.reverse
      end
    end