178

什么是前面带有双 at 符号 ( @@) 的 Ruby 变量?我对以 at 符号开头的变量的理解是它是一个实例变量,就像 PHP 中这样:

PHP版本

class Person {

    public $name;

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

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

红宝石等效

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

双 at 符号是什么@@意思,它与单 at 符号有何不同?

4

5 回答 5

253

带有前缀的变量@实例变量,而带有前缀的变量@@类变量。查看以下示例;它的输出在行尾的注释中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在类之间共享;在 one 的实例中设置值会更改该类的所有其他实例甚至子类的值,其中名为 的变量@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 的评论,您应该非常小心多线程环境中的类变量。

于 2011-05-04T21:42:42.807 回答
40

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

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

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

于 2011-05-04T21:46:39.097 回答
11

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

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

于 2011-05-04T21:34:13.327 回答
2

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

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 或研究生类的特定实例的实例变量.

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

于 2018-08-03T15:23:56.657 回答
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

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

于 2017-05-16T03:44:46.363 回答