常量vs实例变量

时间:2014-08-19 22:42:27

标签: ruby

您何时使用常量而不是类实例变量?它们都具有相同的范围。例子会有所帮助。

3 个答案:

答案 0 :(得分:3)

考虑这个课程:

class Dog
  NUMBER_OF_LEGS = 4

  @dog_counter = 0

  attr_reader :name


  def initialize(name)
    @name = name
  end


  def legs
    NUMBER_OF_LEGS
  end
end

此处,NUMBER_OF_LEGS常量@name实例变量(使用getter方法),@dog_counter是什么叫做类实例变量

在Ruby中,一切都是对象,甚至是类,因此它们可以拥有自己的实例变量。

看看我们如何使用这个类:

dog = Dog.new('Chewbacca')
dog.name
# => 'Chewbacca'

dog.legs
# => 4
Dog::NUMBER_OF_LEGS
# => 4

没关系,但我们拥有访问@dog_counter的直接界面。使用内省方法的唯一方法是使用内省方法:

dog.class.instance_variable_get(:@dog_counter)
# => 0
dog.class.instance_variable_set(:@dog_counter, 1)

dog.class.instance_variable_get(:@dog_counter)
# => 1

dog.class.instance_eval { @dog_counter = 10 }
dog.class.instance_variable_get(:@dog_counter)
# => 10

我们可以做得更好。看看这个其他实现:

class Dog
  @dog_counter = 0

  attr_reader :name

  class << self
    attr_accessor :dog_counter
  end

  def initialize(name)
    @name = name
    self.class.dog_counter += 1
  end

end

现在我们已经定义了一个类访问器(setter和getter),我们也在为每个新实例递增它。界面很简单:

Dog.dog_counter
# => 0

dog_1 = Dog.new('Han')
dog_2 = Dog.new('Luke')


Dog.dog_counter
# => 2
dog_2.class.dog_counter
# => 2

对于正确的类变量,它们在类上作用域,可以通过实例访问。

然而,最大的问题是它们在同一层次结构中的所有类之间共享。设置新值的每个类都将为其所有祖先和后代更新它。

出于这个原因,通常会避免使用它们,并且首选类实例变量(它们是特定于类的)。

class Scientist
  @@greet = "Hello, I'm a Scientist!"

  def greet
    @@greet
  end
end


class Biologist < Scientist
  @@greet = "Hello, I'm a Biologist!"
end


class Physicist < Scientist
  @@greet = "Hello, I'm a Physicist!"
end


class ParticlePhysicist < Physicist
 @@greet = "Hello, I'm a ParticlePhysicist!"
end



biologist = Biologist.new
biologist.greet
# => "Hello, I'm a ParticlePhysicist!"

答案 1 :(得分:1)

他们没有相同的范围。类和它的实例引用相同的常量,但不是同一个实例变量,给定相同的名称。常量也可以从模块的命名空间引用,但实例变量不能。

如果要访问从类方法和实例方法或其他模块引用的内容,则需要一个常量。

你可以反对警告,但这并不好。当你有变化的东西时,最好使用一个变量。

答案 2 :(得分:0)

关于常量和具有相同范围的实例变量:

C = 10

class Dog
  def initialize
    puts C    #=>10
    @x = 1
    puts @x   #=>1
  end
end

d = Dog.new
puts C       #=>10
p @x         #=>nil  It sure doesn't look like @x has the same scope as C.

...

@x = 1

class Dog
  C = 10

  def initialize
    puts C    #=>10
    p @x      #=>nil  Here @x does not have the same scope as C.
  end
end

d = Dog.new
puts @x      #=>1
puts C       #=>Error uninitialized constant.  Here C does not have the same scope as @x.

现在有些术语:

  1. 实例变量会在创建实例变量时将自身附加到self的任何对象上。

  2. 在召唤实例变量时,在任何自身对象中查找实例变量。

  3. 在initialize()中,Dog实例是self。在顶层,self是一个名为'main'的对象。这应该可以让你弄清楚上面的结果。

    @variables将自己附加到实例,而方法将自身附加到类。同一个类的实例共享类中的方法,但类的实例不共享@变量 - 每个实例都有自己的@variables;两个实例甚至不必具有相同的@variables:

    class Dog
      attr_accessor :x, :y, :z
      def initialize
      end
    end
    
    d1 = Dog.new
    d1.x = 10
    
    d2 = Dog.new
    d2.y = 1
    d2.z = 2
    
    
    p d1.instance_variables
    p d2.instance_variables
    
    --output:--
    [:@x]
    [:@y, :@z]
    

    本地变量,例如'x','y'用于存储可以更改的值。

    常量用于存储您不想更改的值。

    @variables 用于将值附加到实例。如果你想让@variable保持不变,那就不要定义一个setter方法,虽然这不是万无一失的,因为ruby允许程序员在他们希望的时候侵犯隐私。