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

什么是 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

符号 @@的双倍是什么意思,它与单个符号有什么不同?

118740 次浏览

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

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

@为前缀的变量是 实例变量,而以 @@为前缀的变量是 类变量。查看下面的示例; 其输出在 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是在类之间共享的; 在一个类的实例中设置值会改变该类的所有其他实例的值,甚至是子类的值,其中一个名为 @shared的变量带有一个 @

[更新]

正如 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 的注释,对于 多线程环境多线程环境中的类变量应该非常小心。

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

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

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

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

如此给予

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

因此,在模块中使用@@ 来表示希望所有使用都通用的变量,在模块中使用@来表示希望在每个使用上下文中单独使用的变量。

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

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 类只有一个相同的@@ 变量,这些类的所有类和实例方法都引用同一个变量。

还有另一种定义类变量的方法,它是在类对象上定义的(请记住,每个类实际上都是类类的实例,但这是另一回事)。可以使用@符号而不是@@ ,但是不能从实例方法访问这些变量。您需要有类方法包装器。

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 (使用单个@符号)始终是一个实例变量,但这并不一定意味着它不是该类的所有实例的单个共享变量。