`class`定义范围中的局部变量与`def`方法范围

时间:2015-10-15 23:47:04

标签: ruby class scope local-variables

在这里,我在类范围中创建一个局部变量:

imageRender()

即使我没有创建class MyClass x = 1 puts x end 的任何实例,也会打印1

我想在某种方法中使用MyClass

x

而我不能。为什么?我得到该类定义创建一个范围,但为什么它不能在方法中访问?该方法的范围不在类的范围内吗?

我可以想象这与创建一个类有关。由于任何类都是class MyClass x = 1 def method puts x end end m = MyClass.new m.method 的对象,因此Class的范围可能是某些MyClass方法的范围,以及Class方法与该实例的耦合方式他们的范围完全不同。

在我看来,我不能仅使用MyClass(如C语言)或{}之类的内容创建范围。我是对的吗?

3 个答案:

答案 0 :(得分:4)

方法的范围在类中。每种方法都有自己的全新范围​​。

每当您使用classmoduledef关键字时,都会创建新的范围。使用括号(如C中所示)不会创建新范围,实际上您不能使用括号任意分组代码行。 Ruby块周围的括号(或do ... end)创建一个块级范围,其中先前在周围范围内创建的变量可用,但在块范围内创建的变量不会转义之后进入周围范围。

实例方法与其他实例方法共享其实例变量的范围。在类定义范围内定义的实例变量在类级单例方法中可用,但在类的实例方法中不可用。

插图:

class Foo
  x = 1  # available only here
  @y = 2 # class-wide value

  def self.class_x
    @x # never set; nil value
  end

  def self.class_y
    @y # class-wide value
  end

  def initialize(z)
    x = 3  # available only here
    @z = z # value for this instance only
  end

  def instance_x
    @x # never set; nil
  end

  def instance_y
    @y # never set; nil
  end

  def instance_z
    @z # value for this instance only
  end
end

Foo.class_x # => nil
Foo.class_y # => 2

Foo.new(0).instance_x # => nil
Foo.new(0).instance_y # => nil

foo3 = Foo.new(3)
foo4 = Foo.new(4)

foo3.instance_z # => 3
foo4.instance_z # => 4

您可以使用类级别的getter从实例中访问类级实例变量。继续上面的例子:

class Foo
  def get_class_y
    self.class.class_y
  end
end

foo = Foo.new(0)
foo.get_class_y # => 2

Ruby中存在“类变量”的概念,它使用@@符号。在实践中,这种语言结构几乎从来没有合理的用例。通常,使用类级实例变量可以更好地实现目标,如此处所示。

答案 1 :(得分:2)

  

在这里,我在类范围中创建一个局部变量:

class MyClass
  x = 1
  puts x
end
     

即使我没有创建1的任何实例,也会打印MyClass

正确。类定义体在读取时执行。它只是像任何其他代码一样的代码,类定义体没有什么特别之处。

问问自己:attr_reader / attr_writer / attr_accessoralias_methodpublic / protected / {{1}等方法如何?否则呢?哎呀,如果在定义类时没有执行,private将如何工作呢? (毕竟,def只是一个表达式,就像任何其他表达式一样!)

这就是为什么你可以做这样的事情:

def
  

我想在某种方法中使用class FileReader if operating_system == :windows def blah; end else def blubb; end end end

x
     

我无法做到。为什么?我得到该类定义创建一个范围,但为什么它不能在方法中访问?这个方法的范围不在类的范围内吗?

不,不是。 Ruby中有4个范围:脚本范围,模块/类定义范围,方法定义范围和块/ lambda范围。只有block / lambdas嵌套,所有其他的都会创建新的范围。

  

我可以想象这与创建一个类有关。由于任何类都是class MyClass x = 1 def method puts x end end m = MyClass.new m.method 的对象,因此Class的范围可能是某些MyClass方法的范围,以及Class方法与该实例的耦合方式他们的范围完全不同。

老实说,我不完全理解你在说什么,但不,类定义范围不是方法定义范围,类定义范围是类定义范围,方法定义范围是方法定义范围。

  

在我看来,我不能仅使用MyClass(如C语言)或{}之类的内容创建范围。我是对的吗?

就像我上面说的:Ruby中有4个范围。没有什么比C中的块范围更好了。"块&#34的Ruby概念与"块的C概念完全不同。")你能得到的最接近的东西是JavaScript启发的立即调用的lambda-literal,如下所示:

do..end

一般来说,Ruby中没有必要这样做。在精心设计的代码中,方法将非常小,跟踪局部变量及其范围和生命周期确实不是什么大问题。

答案 2 :(得分:1)

  

所以只创建一个没有任何实例的类会导致一些问题   实际上在运行时执行(甚至分配可能)?那是非常的   不喜欢C ++。 -

查看此代码:

Dog = Class.new do
  attr_accessor :name

  def initialize(name)
    @name = name
  end
end

如果您执行该代码,则不会有任何输出,但仍然发生了一些事情。例如,创建了一个名为Dog的全局变量,它具有一个值。这是证据:

Dog = Class.new do
  attr_accessor :name

  def initialize(name)
    @name = name
  end
end

dog = Dog.new("Ralph")
puts dog.name

--output:--
Ralph

上面对Dog常量的赋值等同于写:

class Dog
  ...
  ...
end

事实上,ruby遍历类定义中的每一行并执行每一行 - 除非代码行在def中。 def已创建,但def中的代码不会执行,直到调用def。

您将在类定义中看到的一个非常常见的行是:

attr_accessor :name

...可以改写为:

attr_accessor(:name)

...这显然是一个方法调用。当您运行包含类定义的文件时,Ruby会执行该行 - 并调用该方法。 attr_accessor方法然后动态创建并将 getter和setter方法插入到类中。在运行时。是的,这不是C ++的土地 - 欢迎来到NeverNever Land。

  

我得到了类定义创建了一个范围,但为什么不是   方法可以访问吗?

因为这是Matz决定事情的方式:def创建一个新的范围,阻止def之外的变量的可见性。但是,有一些方法可以打开范围门,可以这么说:blocks可以看到在周围范围内定义的变量。查看define_method()

class MyClass
  x = 1

  define_method(:do_stuff) do
    puts x
  end

end

m = MyClass.new
m.do_stuff

--output:--
1

该块是do...end之间的所有内容。在ruby中,块是一个闭包,这意味着当创建一个块时,它会捕获周围范围内的变量,并随之携带这些变量,直到执行该块为止。块就像一个匿名函数,它作为参数传递给方法。

请注意,如果您使用Class.new技巧,则可以打开两个范围门:

x = 1

MyClass = Class.new do

  define_method(:do_stuff) do
    puts x
  end

end

m = MyClass.new
m.do_stuff

--output:--
1

通常情况下,ruby允许程序员做任何他们想做的事,规则被诅咒。