7

我的印象是 Ruby 中的类定义可以重新打开:

class C
  def x
    puts 'x'
  end
end

class C
  def y
    puts 'y'
  end
end

这按预期工作并y添加到原始类定义中。

我很困惑为什么以下代码不能按预期工作:

class D
  x = 12
end

class D
  puts x
end

这将导致NameError异常。为什么重新打开课程时会启动新的本地范围?这似乎有点违反直觉。扩展类时,有什么方法可以继续以前的本地范围?

4

4 回答 4

8

局部变量不与对象相关联,它们与范围相关联。

局部变量是词法范围的。您正在尝试做的事情并不比以下更有效:

def foo
  x = :hack if false  # Ensure that x is a local variable
  p x if $set         # Won't be called the first time through
  $set = x = 42       # Set the local variable and a global flag
  p :done
end

foo                   #=> :done 

foo                   #=> nil (x does not have the value from before)
                      #=> done

在上面,它是相同的方法,在同一个对象上,两次都被执行。self不变。但是,局部变量在调用之间被清除。

重新打开类就像再次调用一个方法:你在同一个self范围内,但你正在开始一个新的本地上下文。当您使用 关闭class D块时end,您的局部变量将被丢弃(除非它们被关闭)。

于 2012-05-04T16:44:27.073 回答
6

在 ruby​​ 中,局部变量只能在它们定义的范围内访问。但是,class关键字会导致一个全新的范围。

class D
  # One scope
  x = 12
end

class D
  # Another scope
  puts x
end

因此,您无法访问第一class部分中定义的局部变量,因为当您离开第一个作用域时,其中的局部变量将被销毁,并且内存被垃圾回收释放。

例如,这也是如此def

于 2012-05-04T16:38:11.600 回答
0

简单的解决方案是将x设为类实例变量。当然,这并不能回答您的范围问题。我相信x不在重新打开的类的范围内(请参阅Detecting the Scope of a Ruby Variable )的原因是因为它的范围从一开始就不是D类的范围。x的范围在D类关闭时结束,因为x既不是实例变量也不是类变量。

我希望这会有所帮助。

于 2012-05-04T16:46:54.170 回答
0

这种行为之所以有意义,部分原因在于元编程能力。您可以使用一些临时变量来存储可用于命名新常量或引用方法名称的数据:

class A
  names, values = get_some_names_and_values()

  names.each_with_index do |name, idx|
    const_set name, value[idx]
  end
end

或者,也许,你需要得到特征类......

class B
  eigenclass = class << self; self; end
  eigenclass.class_eval do
    ...
  end
end

每次重新打开一个类时都有一个新的范围是有意义的,因为在 Ruby 中,您经常在类定义中编写代码,这是要执行的实际代码,打开一个类只是为self. 您不希望类的内容被这些变量污染,否则您将使用类实例变量:

class C
    @answer = 42
end

class C
  p @answer
end
于 2012-05-04T17:08:28.050 回答