Biến @@ có nghĩa là gì trong Ruby?


162

Các biến Ruby có trước với dấu kép ( @@) là gì? Sự hiểu biết của tôi về một biến có trước một dấu hiệu là nó là một biến thể hiện, như thế này trong PHP:

Phiên bản PHP

class Person {

    public $name;

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

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

Tương đương với Ruby

class Person

    def set_name(name)
        @name = name
    end

    def get_name()
        @name
    end
end

Dấu kép @@có ý nghĩa gì, và nó khác với dấu hiệu đơn như thế nào?


103
Tôi không biết, nhưng tôi có cảm giác nó đang nhìn chằm chằm vào tôi. Bây giờ tôi hơi sợ mã hóa trong Ruby ...
corsiKa

2
TL; DR cho công chúng: 99 lần trong số 100, tôi sẽ sử dụng các biến "thể hiện lớp" ( phương thức @bên trong self) chứ không phải biến lớp ( @@). Xem các lý do tại sao trong các câu trả lời dưới đây.
WattsInABox

Câu trả lời:


240

Một biến có tiền tố @là một biến đối tượng , trong khi một biến có tiền tố @@biến lớp . Kiểm tra ví dụ sau đây; đầu ra của nó là trong các ý kiến ​​ở cuối putsdòng:

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

Bạn có thể thấy rằng @@sharedđược chia sẻ giữa các lớp; thiết lập giá trị trong một thể hiện của một thay đổi giá trị cho tất cả các thể hiện khác của lớp đó và ngay cả các lớp con, trong đó một biến có tên @shared, với một @, sẽ không được.

[Cập nhật]

Như Phrogz đã đề cập trong các bình luận, đó là một thành ngữ phổ biến trong Ruby để theo dõi dữ liệu cấp độ lớp với một biến thể hiện trên chính lớp đó . Đây có thể là một chủ đề khó khăn để cuốn lấy tâm trí của bạn và có rất nhiều bài đọc thêm về chủ đề này, nhưng hãy nghĩ về nó như là sửa đổi Classlớp, nhưng chỉ là ví dụ của Classlớp bạn đang làm việc. Một ví dụ:

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

Tôi đã đưa vào Squareví dụ (kết quả đầu ra nil) để chứng minh rằng điều này có thể không hoạt động 100% như bạn mong đợi; các bài viết tôi liên kết ở trên có rất nhiều thông tin bổ sung về đề tài này.

Ngoài ra, hãy nhớ rằng, như với hầu hết các dữ liệu, bạn nên cực kỳ cẩn thận với các biến lớp trong môi trường đa luồng , theo nhận xét của dmarkow.


1
Câu trả lời này sẽ là IMHO hoàn hảo nếu bạn bao gồm mã cho thấy cách bạn có thể sử dụng một biến đối tượng ở cấp lớp để theo dõi dữ liệu cấp độ lớp mà không có hành vi chia sẻ dữ liệu 'lạ' giữa các lớp con.
Phrogz

3
Tôi cũng chỉ ra rằng các biến lớp có thể nguy hiểm / không đáng tin cậy trong môi trường đa luồng (ví dụ: Rails)
Dylan Markow

Hmm ... theo cách nó nghe giống như các biến tĩnh trong PHP, nhưng phần thừa kế thì khác. Tôi không nghĩ PHP có cái gì đó chính xác như thế này.
Andrew

5
Tôi không hiểu ruby class << self endkhối này làm gì, cụ thể là toán tử <<.
davidtingsu

1
cho người khác được nhầm lẫn về class << selfthấy này
kapad

37

@- Biến sơ thẩm của một lớp
@@- Biến lớp, còn được gọi là biến tĩnh trong một số trường hợp

Một biến lớp là một biến được chia sẻ giữa tất cả các thể hiện của một lớp. Điều này có nghĩa là chỉ tồn tại một giá trị biến cho tất cả các đối tượng được khởi tạo từ lớp này. Nếu một thể hiện đối tượng thay đổi giá trị của biến, giá trị mới đó về cơ bản sẽ thay đổi cho tất cả các thể hiện đối tượng khác.

Một cách nghĩ khác về suy nghĩ của các biến lớp là các biến toàn cục trong bối cảnh của một lớp duy nhất. Các biến lớp được khai báo bằng cách thêm tiền tố vào tên biến có hai @ký tự ( @@). Các biến lớp phải được khởi tạo tại thời điểm tạo


10

@@ biểu thị một biến lớp, tức là nó có thể được kế thừa.

Điều này có nghĩa là nếu bạn tạo một lớp con của lớp đó, nó sẽ kế thừa biến. Vì vậy, nếu bạn có một lớp Vehiclevới biến lớp @@number_of_wheelsthì nếu bạn tạo một class Car < Vehiclethì nó cũng sẽ có biến lớp@@number_of_wheels


Điều này có nghĩa là nếu bạn tạo một lớp con của lớp đó, nó sẽ kế thừa biến. Vì vậy, nếu bạn có một lớp Vehiclevới biến lớp @@number_of_wheelsthì nếu bạn tạo một class Car < Vehiclethì nó cũng sẽ có biến lớp@@number_of_wheels
Fareesh Vijayarangam

12
Nếu tôi có một class Vehiclevới @number_of_wheels, thì class Car < Vehiclecũng sẽ có một biến thể hiện được gọi @number_of_wheels. Sự khác biệt chính với các biến lớp là các lớp có cùng một biến, ví dụ thay đổi một biến sẽ thay đổi các biến khác.
Michelle Tilley

1

@ và @@ trong các mô-đun cũng hoạt động khác nhau khi một lớp mở rộng hoặc bao gồm mô-đun đó.

Vì vậy

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 

Sau đó, bạn nhận được kết quả đầu ra dưới đây được hiển thị dưới dạng nhận xét

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

Vì vậy, sử dụng @@ trong các mô-đun cho các biến bạn muốn chung cho tất cả các mục đích sử dụng của chúng và sử dụng @ trong các mô-đun cho các biến bạn muốn tách riêng cho mỗi bối cảnh sử dụng.


1

Các câu trả lời đúng một phần vì @@ thực sự là một biến lớp theo phân cấp lớp có nghĩa là nó được chia sẻ bởi một lớp, các thể hiện của nó và các lớp con cháu của nó và các thể hiện của chúng.

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

Điều này sẽ xuất

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

Vì vậy, chỉ có một biến @@ giống nhau cho các lớp Person, Student và Grad và tất cả các phương thức lớp và thể hiện của các lớp này đều tham chiếu đến cùng một biến.

Có một cách khác để định nghĩa một biến lớp được định nghĩa trên một đối tượng lớp (Hãy nhớ rằng mỗi lớp thực sự là một thể hiện của một cái gì đó thực sự là lớp Class nhưng nó là một câu chuyện khác). Bạn sử dụng ký hiệu @ thay vì @@ nhưng bạn không thể truy cập các biến này từ các phương thức thể hiện. Bạn cần phải có các hàm bao phương thức lớp.

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(",")

Ở đây, @people là một đơn cho mỗi lớp thay vì phân cấp lớp vì nó thực sự là một biến được lưu trữ trên mỗi thể hiện của lớp. Đây là đầu ra:

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

Một điểm khác biệt quan trọng là, bạn không thể truy cập trực tiếp các biến lớp này (hoặc biến đối tượng lớp mà bạn có thể nói) từ các phương thức cá thể vì @people trong một phương thức cá thể sẽ tham chiếu đến một biến thể hiện của cá thể cụ thể đó của các lớp Person hoặc Student hoặc Grad .

Vì vậy, trong khi các câu trả lời khác nói chính xác rằng @myvariable (với ký hiệu @ duy nhất) luôn là một biến thể hiện, thì điều đó không nhất thiết có nghĩa là nó không phải là một biến được chia sẻ duy nhất cho tất cả các phiên bản của lớp đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.