@@ variable在Ruby中是什么意思?


162

Ruby变量在符号(@@)前面加双的是什么?我对以at符号开头的变量的理解是,它是一个实例变量,例如PHP:

PHP版本

class Person {

    public $name;

    public function setName($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

相当于Ruby

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

符号双的@@含义是什么?与符号双的区别是什么?


103
我不知道,但是我感觉到它在盯着我。我现在有点害怕用Ruby编码...
corsiKa 2011年

2
TL; DR面向公众:在100的99倍中,我将使用“类实例”变量(@self方法内部)而不是类变量(@@)。请在下面的答案中查看所有原因。
WattsInABox

Answers:


240

带前缀的变量@实例变量,带前缀的@@类变量。查看以下示例;其输出在各puts行末尾的注释中:

class Test
  @@shared = 1

  def value
    @@shared
  end

  def value=(value)
    @@shared = value
  end
end

class AnotherTest < Test; end

t = Test.new
puts "t.value is #{t.value}" # 1
t.value = 2
puts "t.value is #{t.value}" # 2

x = Test.new
puts "x.value is #{x.value}" # 2

a = AnotherTest.new
puts "a.value is #{a.value}" # 2
a.value = 3
puts "a.value is #{a.value}" # 3
puts "t.value is #{t.value}" # 3
puts "x.value is #{x.value}" # 3

您可以看到@@shared在类之间共享。将值设置为1的实例将更改该类甚至子类的所有其他实例的值,其中名为的变量(不@shared带one @)将不会。

[更新]

正如Phrogz在评论中提到的那样,这是Ruby中常见的习惯用法,即使用类本身的实例变量来跟踪类级别的数据。这可能是一个棘手的问题,需要您多加思考,并且有很多关于该主题的附加阅读,但是可以将其视为修改Class类,但修改Class您正在使用的类的实例。一个例子:

class Polygon
  class << self
    attr_accessor :sides
  end
end

class Triangle < Polygon
  @sides = 3
end

class Rectangle < Polygon
  @sides = 4
end

class Square < Rectangle
end

class Hexagon < Polygon
  @sides = 6
end

puts "Triangle.sides:  #{Triangle.sides.inspect}"  # 3
puts "Rectangle.sides: #{Rectangle.sides.inspect}" # 4
puts "Square.sides:    #{Square.sides.inspect}"    # nil
puts "Hexagon.sides:   #{Hexagon.sides.inspect}"   # 6

我包含了Square示例(输出nil),以证明这可能不会达到您期望的100%;我上面链接文章中有很多与此主题相关的其他信息。

还要记住,与大多数数据一样,根据dmarkow的评论,在多线程环境中使用类变量时要格外小心。


1
如果您提供了显示如何在类级别使用实例变量来跟踪类级别数据而又没有子类之间共享数据的“怪异”行为的代码,则此答案将是完美的恕我直言。
Phrogz

3
我还要指出,类变量在多线程环境(例如Rails)中可能很危险/不可靠
Dylan Markow 2011年

嗯...在某种程度上听起来像PHP中的静态变量,但是继承部分却有所不同。我不认为PHP具有这样的功能。
安德鲁

5
我不明白该ruby class << self end块的作用,特别是<<操作符。
davidtingsu

1
对于其他人感到困惑class << self看到
kapad

37

@-类的实例变量
@@-类变量,在某些情况下也称为静态变量

类变量是在类的所有实例之间共享的变量。这意味着从该类实例化的所有对象仅存在一个变量值。如果一个对象实例更改了变量的值,则对于所有其他对象实例,该新值将实质上发生更改。

关于类变量的另一种思考方式是在单个类的上下文中作为全局变量。类变量通过在变量名前添加两个@字符(@@)来声明。类变量必须在创建时初始化


10

@@ 表示一个类变量,即它可以被继承。

这意味着,如果您创建该类的子类,它将继承该变量。因此,如果您有一个Vehicle带有class变量的类,@@number_of_wheels那么如果您创建一个class ,class Car < Vehicle那么它也将具有class变量@@number_of_wheels


这意味着,如果您创建该类的子类,它将继承该变量。因此,如果您有一个Vehicle带有类变量的类,@@number_of_wheels那么如果您创建一个类,class Car < Vehicle那么它也将具有该类变量@@number_of_wheels
Fareesh Vijayarangam 2011年

12
如果我有一个class Vehiclewith @number_of_wheels,那么class Car < Vehicle还将有一个名为的实例变量@number_of_wheels。类变量的主要区别在于,类具有相同的变量,例如,更改一个会更改另一个。
Michelle Tilley

1

当类扩展或包含该模块时,模块中的@和@@的工作方式也不同。

因此给定

module A
    @a = 'module'
    @@a = 'module'

    def get1
        @a          
    end     

    def get2
        @@a         
    end     

    def set1(a) 
        @a = a      
    end     

    def set2(a) 
        @@a = a     
    end     

    def self.set1(a)
        @a = a      
    end     

    def self.set2(a)
        @@a = a     
    end     
end 

然后,您将获得以下输出,显示为注释

class X
    extend A

    puts get1.inspect # nil
    puts get2.inspect # "module"

    @a = 'class' 
    @@a = 'class' 

    puts get1.inspect # "class"
    puts get2.inspect # "module"

    set1('set')
    set2('set')

    puts get1.inspect # "set" 
    puts get2.inspect # "set" 

    A.set1('sset')
    A.set2('sset')

    puts get1.inspect # "set" 
    puts get2.inspect # "sset"
end 

class Y
    include A

    def doit
        puts get1.inspect # nil
        puts get2.inspect # "module"

        @a = 'class'
        @@a = 'class'

        puts get1.inspect # "class"
        puts get2.inspect # "class"

        set1('set')
        set2('set')

        puts get1.inspect # "set"
        puts get2.inspect # "set"

        A.set1('sset')
        A.set2('sset')

        puts get1.inspect # "set"
        puts get2.inspect # "sset"
    end
end

Y.new.doit

因此,在模块中使用@@表示您希望对其所有使用通用的变量,在模块中使用@@表示想要针对每种使用上下文分开的变量。


1

答案部分正确,因为@@实际上是每个类层次结构的类变量,这意味着它由类,其实例,其后代类及其实例共享。

class Person
  @@people = []

  def initialize
    @@people << self
  end

  def self.people
    @@people
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Student.new

puts Graduate.people

这将输出

#<Person:0x007fa70fa24870>
#<Student:0x007fa70fa24848>

因此,Person,Student和Graduate类只有一个相同的@@ variable,并且这些类的所有类和实例方法都引用相同的变量。

还有另一种定义在类对象上定义的类变量的方法(请记住,每个类实际上是某个事物的实例,而该事物实际上是Class类,但这是另一回事了)。您使用@表示法而不是@@,但无法从实例方法访问这些变量。您需要具有类方法包装器。

class Person

  def initialize
    self.class.add_person self
  end

  def self.people
    @people
  end

  def self.add_person instance
    @people ||= []
    @people << instance
  end
end

class Student < Person
end

class Graduate < Student
end

Person.new
Person.new
Student.new
Student.new
Graduate.new
Graduate.new

puts Student.people.join(",")
puts Person.people.join(",")
puts Graduate.people.join(",")

在这里,@ people每个类都是单个类,而不是类层次结构,因为它实际上是存储在每个类实例上的变量。这是输出:

#<Student:0x007f8e9d2267e8>,#<Student:0x007f8e9d21ff38>
#<Person:0x007f8e9d226158>,#<Person:0x007f8e9d226608>
#<Graduate:0x007f8e9d21fec0>,#<Graduate:0x007f8e9d21fdf8> 

一个重要的区别是,您不能直接从实例方法访问这些类变量(或您可以说的类实例变量),因为实例方法中的@people将引用Person或Student或Graduate类的特定实例的实例变量。 。

因此,尽管其他答案正确地指出@myvariable(带有单个@表示法)始终是一个实例变量,但这并不一定意味着它不是该类所有实例的单个共享变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.