ตัวแปร @@ หมายถึงอะไรในรูบี


162

ตัวแปร Ruby นำหน้าด้วย double at signs ( @@) คืออะไร ความเข้าใจของฉันเกี่ยวกับตัวแปรที่นำหน้าด้วยเครื่องหมาย 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

Double at sign @@หมายถึงอะไรและแตกต่างจาก single at sign อย่างไร


103
ฉันไม่รู้ แต่ฉันรู้สึกว่ามันกำลังจ้องมองที่ฉัน ฉันกลัวเล็ก ๆ น้อย ๆ ในการใช้รหัสในตอนนี้ ... ทับทิม
corsiKa

2
TL; DR สำหรับสาธารณะ: 99 ครั้งจาก 100 ฉันจะใช้ตัวแปร "คลาสอินสแตนซ์" ( @ภายในselfเมธอด) ไม่ใช่ตัวแปรคลาส ( @@) ดูบทสวดของเหตุผลว่าทำไมในคำตอบด้านล่าง
WattsInABox

คำตอบ:


240

ตัวแปรนำหน้าด้วย@เป็นตัวแปรเช่นในขณะที่หนึ่งนำหน้าด้วย@@เป็นตัวแปรระดับ ลองดูตัวอย่างต่อไปนี้ เอาท์พุทมันอยู่ในความคิดเห็นในตอนท้ายของ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หนึ่ง@จะไม่เป็น

[Update]

ในฐานะที่เป็น Phrogz กล่าวถึงในความคิดเห็นที่มันเป็นสำนวนที่พบบ่อยในทับทิมเพื่อติดตามข้อมูลระดับระดับที่มีตัวแปรเช่นในชั้นเอง นี่อาจเป็นเรื่องที่ยุ่งยากที่จะทำให้คุณคิดมากและมีเรื่องอ่านเพิ่มเติมมากมายแต่คิดว่าเป็นการดัดแปลง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


1
คำตอบนี้จะสมบูรณ์แบบ IMHO หากคุณรวมรหัสที่แสดงวิธีการใช้ตัวแปรอินสแตนซ์ที่ระดับชั้นเพื่อติดตามข้อมูลระดับชั้นเรียนโดยไม่มีพฤติกรรม 'แปลก' ของการแบ่งปันข้อมูลระหว่างคลาสย่อย
Phrogz

3
ฉันยังต้องการชี้ให้เห็นว่าตัวแปรระดับสามารถเป็นอันตราย / ไม่น่าเชื่อถือในสภาพแวดล้อมแบบมัลติเธรด (เช่นทางรถไฟ)
ดีแลน Markow

อืม ... ดูเหมือนว่าตัวแปรคงที่ใน PHP แต่ในส่วนของการสืบทอดนั้นแตกต่างกัน ฉันไม่คิดว่า PHP มีบางอย่างเช่นนี้
Andrew

5
ฉันไม่เข้าใจว่าruby class << self endบล็อกทำอะไรโดยเฉพาะตัวดำเนินการ <<
davidtingsu

1
สำหรับผู้อื่นที่สับสนเกี่ยวกับการclass << selfดูนี้
kapad

37

@- ตัวแปรอินสแตนซ์ของคลาส
@@- ตัวแปรคลาสเรียกอีกอย่างว่าตัวแปรแบบคงที่ในบางกรณี

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

อีกวิธีหนึ่งในการคิดเกี่ยวกับการคิดตัวแปรคลาสคือตัวแปรทั่วโลกภายในบริบทของคลาสเดียว ตัวแปรคลาสถูกประกาศโดยนำหน้าชื่อตัวแปรด้วย@อักขระสองตัว ( @@) ตัวแปรคลาสจะต้องเริ่มต้น ณ เวลาที่สร้าง


10

@@ หมายถึงตัวแปรระดับคือมันสามารถสืบทอด

ซึ่งหมายความว่าถ้าคุณสร้างคลาสย่อยของคลาสนั้นมันจะสืบทอดตัวแปร ดังนั้นหากคุณมีคลาสที่Vehicleมีคลาสตัวแปร@@number_of_wheelsดังนั้นถ้าคุณสร้างคลาสขึ้นclass Car < Vehicleมามันก็จะมีคลาสตัวแปรเช่นกัน@@number_of_wheels


ซึ่งหมายความว่าถ้าคุณสร้างคลาสย่อยของคลาสนั้นมันจะสืบทอดตัวแปร ดังนั้นถ้าคุณมีคลาสที่Vehicleมีคลาสตัวแปร@@number_of_wheelsดังนั้นถ้าคุณสร้างคลาสขึ้นclass Car < Vehicleมามันก็จะมีคลาสตัวแปรด้วย@@number_of_wheels
Fareesh Vijayarangam

12
ถ้าฉันมีclass Vehicleด้วย@number_of_wheelsแล้วยังจะมีตัวแปรเช่นที่เรียกว่าclass Car < Vehicle @number_of_wheelsความแตกต่างที่สำคัญกับตัวแปรคลาสคือคลาสมีตัวแปรเดียวกันเช่นการเปลี่ยนแปลงหนึ่งการเปลี่ยนแปลงอื่น
Michelle Tilley

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

ดังนั้นใช้ @@ ในโมดูลสำหรับตัวแปรที่คุณต้องการใช้ร่วมกับการใช้งานทั้งหมดและใช้ @ ในโมดูลสำหรับตัวแปรที่คุณต้องการแยกต่างหากสำหรับทุกบริบทการใช้งาน


1

คำตอบนั้นถูกต้องเพียงบางส่วนเนื่องจาก @@ เป็นตัวแปรคลาสซึ่งเป็นลำดับชั้นตามความหมายซึ่งแบ่งใช้โดยคลาสอินสแตนซ์และคลาสที่สืบทอดและอินสแตนซ์ของพวกเขา

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 และบัณฑิตและวิธีการเรียนและอินสแตนซ์ทั้งหมดของคลาสเหล่านี้อ้างอิงถึงตัวแปรเดียวกัน

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

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 ในวิธีการอินสแตนซ์จะอ้างถึงตัวแปรอินสแตนซ์ของ .

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.