Sự khác biệt giữa biến lớp và biến cá thể lớp?


Câu trả lời:


151

Một biến lớp ( @@) được chia sẻ giữa lớp và tất cả các biến lớp con của nó. Một biến cá thể của lớp ( @) không được chia sẻ bởi con cháu của lớp.


Biến lớp ( @@)

Hãy có một lớp Foo với một biến lớp @@ivà các trình truy cập để đọc và ghi @@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

  def self.i=(value)
    @@i = value
  end

end

Và một lớp dẫn xuất:

class Bar < Foo
end

Chúng tôi thấy rằng Foo và Bar có cùng giá trị đối với @@i:

p Foo.i    # => 1
p Bar.i    # => 1

Và thay đổi @@itrong một thay đổi nó trong cả hai:

Bar.i = 2
p Foo.i    # => 2
p Bar.i    # => 2

Biến cá thể lớp ( @)

Hãy tạo một lớp đơn giản với biến cá thể của lớp @ivà các trình truy cập để đọc và ghi @i:

class Foo

  @i = 1

  def self.i
    @i
  end

  def self.i=(value)
    @i = value
  end

end

Và một lớp dẫn xuất:

class Bar < Foo
end

Chúng ta thấy rằng mặc dù Bar kế thừa các trình truy cập cho @i, nó không kế thừa @ichính nó:

p Foo.i    # => 1
p Bar.i    # => nil

Chúng tôi có thể đặt Bar's @imà không ảnh hưởng đến Foo's @i:

Bar.i = 2
p Foo.i    # => 1
p Bar.i    # => 2

1
Tại sao trả về các biến cá thể bằng cách sử dụng các phương thức lớp? Bạn có thường xuyên gặp phải trường hợp này không?
sekmo

1
@sekmo Các trình truy cập trong các ví dụ này trả về các biến cá thể của lớp , thuộc về lớp hoặc các biến lớp thuộc về phân cấp lớp. Chúng không trả về các biến thể hiện thuần túy thuộc về các thể hiện của lớp. Các thuật ngữ "biến instance", "class instance varaible" và "class variable" khá khó hiểu phải không?
Wayne Conrad

72

Đầu tiên, bạn phải hiểu rằng các lớp cũng là các cá thể - các cá thể của Classlớp.

Khi bạn hiểu điều đó, bạn có thể hiểu rằng một lớp có thể có các biến cá thể được liên kết với nó giống như một đối tượng thông thường (read: non-class) có thể.

Hello = Class.new

# setting an instance variable on the Hello class
Hello.instance_variable_set(:@var, "good morning!")

# getting an instance variable on the Hello class
Hello.instance_variable_get(:@var) #=> "good morning!"

Lưu ý rằng một biến cá thể trên Hellohoàn toàn không liên quan và khác biệt với một biến cá thể trên một phiên bản củaHello

hello = Hello.new

# setting an instance variable on an instance of Hello
hello.instance_variable_set(:@var, :"bad evening!")

# getting an instance variable on an instance of Hello
hello.instance_variable_get(:@var) #=> "bad evening!")

# see that it's distinct from @var on Hello
Hello.instance_variable_get(:@var) #=> "good morning!"

Mặt khác, một biến lớp là một kiểu kết hợp của hai biến trên, vì nó có thể truy cập được trên Hellochính nó và các thể hiện của nó, cũng như trên các lớp con Hellovà các thể hiện của chúng:

HelloChild = Class.new(Hello)
Hello.class_variable_set(:@@class_var, "strange day!")
hello = Hello.new
hello_child = HelloChild.new

Hello.class_variable_get(:@@class_var) #=> "strange day!"
HelloChild.class_variable_get(:@@class_var) #=> "strange day!"
hello.singleton_class.class_variable_get(:@@class_var) #=> "strange day!"
hello_child.singleton_class.class_variable_get(:@@class_Var) #=> "strange day!"

Nhiều người nói hãy tránh class variablesvì hành vi kỳ lạ trên, đồng thời khuyến cáo sử dụng class instance variablesthay thế.


2
+1 Siêu giải thích! Đây là điều mà mọi lập trình viên mới làm quen với Ruby nên hiểu.
TomZ

0

Ngoài ra, tôi muốn nói thêm rằng bạn có thể có quyền truy cập vào biến lớp ( @@) từ bất kỳ trường hợp nào của lớp

class Foo
  def set_name
    @@name = 'Nik'
  end

  def get_name
    @@name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => Nik

Nhưng bạn không thể làm như vậy đối với biến cá thể lớp ( @)

class Foo
  def set_name
    @name = 'Nik'
  end

  def get_name
    @name
  end
end


a = Foo.new
a.set_name
p a.get_name # => Nik
b = Foo.new
p b.get_name # => nil

-1: Điều này không chính xác: các biến thể hiện của lớp có thể được truy cập từ mọi thể hiện của lớp đó (với điều kiện là có trình truy cập). Bên cạnh đó, ví dụ của bạn hiển thị các biến cá thể thông thường, không phải biến cá thể lớp. Các biến cá thể lớp được khai báo bên ngoài các phương thức, và về cơ bản khác với cả biến cá thể thông thường và biến lớp.
The Pellmeister

Làm cách nào để bạn có thể truy cập vào các biến cá thể của lớp từ mọi phiên bản của lớp đó?
Evan Ross

Bạn đã đọc câu trả lời của Wayne Conrad chưa? Điều đó giải thích theo nghĩa đen. stackoverflow.com/a/3803089/439592
The Pellmeister Ngày

Tôi biết bạn có thể truy cập từ các phương thức của lớp như Foo.i, nhưng làm thế nào bạn có thể làm điều tương tự cho mọi trường hợp của lớp này như thế Foo.new.inào?
Evan Ross

Bằng cách sử dụng #class. Cá nhân tôi nghĩ rằng #classtập quán khi được gọi trên bất cứ thứ gì nhưng chỉ selflà mùi mã. Bạn thậm chí có thể tiến thêm một bước nữa và triển khai các trình truy cập cá thể ii=ủy quyền cho các đối tượng #classtương đương của chúng , trong trường hợp đó bạn có thể làm được Foo.new.i. Tôi không khuyên bạn nên làm điều đó vì nó tạo ra một giao diện khó hiểu, cho thấy rằng bạn đang sửa đổi một thành viên đối tượng.
The Pellmeister
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.