关于 ruby??:Class::new 块中的引用方法参数

Reference Method Parameter in Class::new block

我正在尝试根据以下问题在 Ruby 中使用元编程创建类:Dynamicly define named classes in Ruby。一切进展顺利,除了似乎在 Class.new 的块中无法引用奇怪的方法参数。

我有这个

1
2
3
4
5
6
7
8
9
10
11
12
class A; end

module B
  def self.class_with_method(class_name, method_return)
    klass = Class.new(A) do
      def example
        method_return
      end
    end
    B.const_set class_name, klass
  end
end

但是当我有上面的然后用

测试它

1
2
B.class_with_method 'ExampleClass', 23
B::ExampleClass.new.example

给我

undefined local variable or method `method_return' for # (NameError)

这很奇怪,因为如果我要这样做

1
2
3
4
5
6
def add_number(number)
  [1, 2, 3, 4].map {|i| i + number}
end

add_number(2)
# => [3, 4, 5, 6]

很明显,块可以接受方法参数。

有没有办法在块内将 method_return 传递给 def example


这一点也不奇怪,这是方法定义的常规行为:

1
2
3
4
5
6
7
8
class A
  x = 3
  def foo
    x
  end
end

A.new.foo # NameError: undefined local variable or method `x'

方法定义不会从其外部范围捕获局部变量。这通常是好的:通常您不希望方法的结果依赖于其定义时存在的某些随机状态,您希望它依赖于您调用它的对象!

当然,当您进行元编程时除外。在这种情况下,捕获局部变量很有用,因此您可以使用与其他地方相同的机制显式捕获它们:块。

1
2
3
define_method :example do
  method_return
end

因为 method_return 是一个变量,所以你不能将它向下传播到 example 方法,除非你在另一个上下文中使用它。您可以在类级变量中捕获它:

1
2
3
4
5
6
7
klass = Class.new(A) do
  @@method_return = method_return

  def example
    @@method_return
  end
end

您还可以动态定义方法,以便块将 method_return 变量捕获为闭包:

1
2
3
4
5
klass = Class.new(A) do
  define_method(:example) do
    method_return
  end
end

当使用 def example 时,块范围会发生变化并变得完全不同。


def 结构关闭了它的作用域。局部变量不能超越这一点。要执行您想要的操作,请改用 define_method