ความแตกต่างระหว่างตัวแปรคลาสและตัวแปรอินสแตนซ์คลาส?


คำตอบ:


152

ตัวแปรคลาส ( @@) ถูกแบ่งใช้ระหว่างคลาสและลูกหลานทั้งหมด ตัวแปรอินสแตนซ์คลาส ( @) ไม่ได้แบ่งใช้โดยลูกหลานของคลาส


ตัวแปรคลาส ( @@)

มามีคลาส Foo พร้อมตัวแปรคลาส@@iและ accessors สำหรับการอ่านและเขียน@@i:

class Foo

  @@i = 1

  def self.i
    @@i
  end

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

end

และคลาสที่ได้รับ:

class Bar < Foo
end

เราเห็นว่า Foo และ Bar มีค่าเท่ากันสำหรับ@@i:

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

และการเปลี่ยนแปลง@@iในครั้งเดียวการเปลี่ยนแปลงในทั้งสอง:

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

ตัวแปรอินสแตนซ์คลาส ( @)

มาสร้างคลาสง่ายๆด้วยตัวแปรอินสแตนซ์คลาส@iและตัวเข้าถึงสำหรับการอ่านและเขียน@i:

class Foo

  @i = 1

  def self.i
    @i
  end

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

end

และคลาสที่ได้รับ:

class Bar < Foo
end

เราเห็นว่าแม้ว่า Bar จะสืบทอด accessors สำหรับ@iแต่ก็ไม่ได้สืบทอด@iตัวเอง:

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

เราสามารถตั้งค่า Bar ได้@iโดยไม่กระทบกับ Foo @i:

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

1
เหตุใดจึงส่งคืนตัวแปรอินสแตนซ์โดยใช้เมธอดคลาส คุณมักจะเจอสถานการณ์นี้หรือไม่?
sekmo

1
@sekmo ตัวเข้าถึงในตัวอย่างเหล่านี้ส่งคืนตัวแปรอินสแตนซ์คลาสซึ่งเป็นของคลาสหรือตัวแปรคลาสซึ่งเป็นของลำดับชั้นคลาส พวกเขาไม่ส่งคืนตัวแปรอินสแตนซ์ธรรมดาซึ่งเป็นของอินสแตนซ์ของคลาส คำว่า "instance variable" "class instance varaible" และ "class variable" ค่อนข้างสับสนใช่หรือไม่
Wayne Conrad

72

ก่อนอื่นคุณต้องเข้าใจว่าคลาสก็เป็นอินสแตนซ์เช่นกัน - อินสแตนซ์ของClassคลาส

เมื่อคุณเข้าใจแล้วคุณจะเข้าใจว่าคลาสสามารถมีตัวแปรอินสแตนซ์ที่เชื่อมโยงกับมันได้เช่นเดียวกับที่ออบเจ็กต์ปกติ (อ่าน: ไม่ใช่คลาส) สามารถทำได้

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!"

โปรดทราบว่าตัวแปรอินสแตนซ์บนHelloไม่เกี่ยวข้องโดยสิ้นเชิงและแตกต่างจากตัวแปรอินสแตนซ์บนอินสแตนซ์ของHello

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!"

ตัวแปรระดับบนมืออื่น ๆ เป็นชนิดของการรวมกันของสองข้างต้นเป็นมันสามารถเข้าถึงได้บนHelloตัวเองและกรณีของตนเช่นเดียวกับคลาสย่อยHelloและอินสแตนซ์ของพวกเขา

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!"

หลายคนบอกว่าให้หลีกเลี่ยงclass variablesเนื่องจากพฤติกรรมแปลก ๆ ข้างต้นและแนะนำให้ใช้class instance variablesแทน


2
+1 สุดยอดคำอธิบาย! นี่คือสิ่งที่โปรแกรมเมอร์มือใหม่ทุกคนที่เพิ่งรู้จัก Ruby ควรแยกย่อย
TomZ

0

นอกจากนี้ฉันต้องการเพิ่มว่าคุณสามารถเข้าถึงตัวแปรคลาส ( @@) จากอินสแตนซ์ของคลาสใดก็ได้

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

แต่คุณไม่สามารถทำเช่นเดียวกันกับตัวแปรอินสแตนซ์คลาส ( @)

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: สิ่งนี้ไม่ถูกต้อง: ตัวแปรอินสแตนซ์คลาสสามารถเข้าถึงได้จากทุกอินสแตนซ์ของคลาสนั้น (โดยมีเงื่อนไขว่ามีตัวเข้าถึงอยู่) นอกจากนี้ตัวอย่างของคุณยังแสดงตัวแปรอินสแตนซ์ปกติไม่ใช่ตัวแปรอินสแตนซ์คลาส ตัวแปรอินสแตนซ์คลาสถูกประกาศนอกวิธีการและมีความแตกต่างโดยพื้นฐานจากตัวแปรอินสแตนซ์ปกติและตัวแปรคลาส
The Pellmeister

คุณจะเข้าถึงตัวแปรอินสแตนซ์คลาสจากทุกอินสแตนซ์ของคลาสนั้นได้อย่างไร
Evan Ross

คุณอ่านคำตอบของ Wayne Conrad หรือยัง? นั่นอธิบายได้อย่างแท้จริง stackoverflow.com/a/3803089/439592
The Pellmeister

ฉันรู้ว่าคุณสามารถเข้าถึงได้จากเมธอดคลาสเช่นFoo.iแต่คุณจะทำเช่นเดียวกันกับทุกอินสแตนซ์ของคลาสนี้ได้Foo.new.iอย่างไร
Evan Ross

โดยใช้#class. โดยส่วนตัวฉันคิดว่าการ#classใช้งานเมื่อถูกเรียกใช้อะไรก็ได้ แต่selfเป็นกลิ่นรหัส คุณยังสามารถไปขั้นตอนต่อไปและดำเนินการเช่น accessors iและi=ผู้ร่วมประชุมที่พวกเขาเทียบเท่าซึ่งในกรณีที่คุณสามารถทำได้#class Foo.new.iฉันไม่แนะนำให้ทำเช่นนั้นเนื่องจากมันสร้างอินเทอร์เฟซที่สับสนแนะนำว่าคุณกำลังแก้ไขสมาชิกออบเจ็กต์
The Pellmeister
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.