有没有办法从该实例内部为Ruby类的实例创建方法?

时间:2010-06-11 23:53:56

标签: ruby

class Example定义为:

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

在致电Example.new; Example.new时,我得到warning: method redefined; discarding old say_hello。我总结说,这必须是因为它在实际的类中定义了一个方法(从语法上来说这是有意义的)。当然,如果Example的多个实例在其方法中具有不同的值,那么这将是灾难性的。

有没有办法只为该实例内部的类实例创建方法?

3 个答案:

答案 0 :(得分:69)

您需要获取对实例的单例类的引用,该类包含所有特定于实例的内容,并在其上定义方法。在红宝石1.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
然而,Ruby 1.9提供了一种更简单的方法。

Ruby 1.9

class Example
  def initialize(test='hey')
    define_singleton_method :say_hello, lambda { test }
  end
end

答案 1 :(得分:39)

首先,小样式提示:

self.class.send(:define_method, :say_hello, lambda { test })

通过在Ruby 1.9中使用新的proc文字,可以使这个看起来更好一些:

self.class.send(:define_method, :say_hello, -> { test })

但你不需要那样。 Ruby有一些名为 blocks 的东西,它们基本上是一段代码,可以作为参数传递给方法。实际上,您已经使用了块,因为lambda只是一个将块作为参数并返回Proc的方法。但是,define_method 已经会占用一个块,因此无需将块传递给lambda,而Proc会将其转换为传递给{define_method的{​​{1}} 1}}然后将其转换回块:

self.class.send(:define_method, :say_hello) { test }

正如您已经注意到的那样,您正在错误的类上定义方法。您在Example类上定义它,因为在initialize之类的实例方法中,self是@ mikej中的当前对象(即ex1ex2例如),这意味着self.classex1的类,Example。所以,你一遍又一遍地覆盖同样的方法。

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

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 }

这可以按预期工作:

ex1 = Example.new('ex1')
ex2 = Example.new('ex2')
ex1.say_hello            # => ex1
ex2.say_hello            # => ex2

在Ruby 1.9中,有一种方法可以做到这一点:

define_singleton_method(:say_hello) { test }

现在,这可以按照你想要的方式工作,但这里有一个更高级别的问题:这不是Ruby代码。它是Ruby 语法,但它不是Ruby代码,它是Scheme。

现在,Scheme是一种辉煌的语言,用Ruby语法编写Scheme代码肯定不是一件坏事。它胜过用Ruby语法编写Java或PHP代码,或者像昨天的StackOverflow问题中的情况一样,使用Ruby语法中的Fortran-57代码。但它不如用Ruby语法编写 Ruby 代码那么好。

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

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

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

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

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

  def say_hello
    @test
  end
end

答案 2 :(得分:8)

我知道这是两年前被问到的,但我想补充一个答案。 string = "String" string.instance_eval do def new_method self.reverse end end 将有助于将方法添加到实例对象

{{1}}