Ruby: การเรียกคลาสเมธอดจากอินสแตนซ์


347

ใน Ruby คุณจะเรียกวิธีการเรียนจากอินสแตนซ์ของคลาสนั้นได้อย่างไร บอกว่าฉันมี

class Truck
  def self.default_make
    # Class method.
    "mac"
  end

  def initialize
    # Instance method.
    Truck.default_make  # gets the default via the class's method.
    # But: I wish to avoid mentioning Truck. Seems I'm repeating myself.
  end
end

บรรทัดTruck.default_makeดึงค่าเริ่มต้น แต่มีวิธีพูดแบบนี้โดยไม่เอ่ยถึงTruckหรือไม่? ดูเหมือนว่าควรจะมี

คำตอบ:


563

self.class.whateverมากกว่าหมายถึงชื่อที่แท้จริงของการเรียนภายในวิธีการเช่นคุณก็สามารถโทร

class Foo
    def self.some_class_method
        puts self
    end

    def some_instance_method
        self.class.some_class_method
    end
end

print "Class method: "
Foo.some_class_method

print "Instance method: "
Foo.new.some_instance_method

ขาออก:

วิธีการเรียน: ฟู
วิธีอินสแตนซ์: Foo

7
ฉันต้องการจะดูทางลัดในทับทิมเพื่อเรียกวิธีการเรียนจากตัวอย่าง เช่น:> some_class_method แทน self.class.some_class_method
phoet

7
ในขณะที่นี่คือคำตอบที่ถูกต้องมันเป็นความละอายที่ "self.class" พิมพ์ได้มากขึ้นและอ่านง่ายกว่าชื่อคลาส "รถบรรทุก" โอ้ดี ....
แมตต์คอน

22
@ MattConnolly เป็นญาติกันถ้าชื่อคลาสของคุณSalesforceSyncJobสั้นกว่านั้น)
drewish

29
@MattConnolly ใช้self.classไม่จำเป็นต้องค้นหา / แทนที่หากคุณเปลี่ยนชื่อคลาส
Gus Shortz

8
@GusShortz จริง นอกจากนี้ self.class ยังทำงานได้ดีขึ้นหากมีคลาสย่อย
Matt Connolly

183

การใช้self.class.blahไม่เหมือนกับการใช้ClassName.blahเมื่อพูดถึงการสืบทอด

class Truck
  def self.default_make
    "mac"
  end

  def make1
    self.class.default_make
  end

  def make2
    Truck.default_make
  end
end


class BigTruck < Truck
  def self.default_make
    "bigmac"
  end
end

ruby-1.9.3-p0 :021 > b=BigTruck.new
 => #<BigTruck:0x0000000307f348> 
ruby-1.9.3-p0 :022 > b.make1
 => "bigmac" 
ruby-1.9.3-p0 :023 > b.make2
 => "mac" 

58
นี่น่าจะเป็นการตอบสนองต่อคำตอบที่ได้รับการยอมรับมากกว่าที่จะตอบคำถาม
zhon

16
@zohn - จริง แต่นี่ยังเป็นบริบทที่มีประโยชน์เมื่อพิจารณาว่าจะใช้อะไร
Matt Sanders

1
@ MattSanders เพียงแค่ใช้ความคิดเห็นในกรณีเหล่านั้น
nandilugio

1
@hlcs self.classถูกต้องเพื่อรักษามรดก แม้ว่าจะmake1()มีการกำหนดไว้Truckมันเป็นวิธีการอ้างอิงBigTruckของคลาส
ไกเซอร์ Shahid

14

ในการเข้าถึงวิธีการเรียนภายในวิธีการอินสแตนซ์ทำต่อไปนี้:

self.class.default_make

นี่คือทางเลือกอื่นสำหรับปัญหาของคุณ:

class Truck

  attr_accessor :make, :year

  def self.default_make
    "Toyota"
  end

  def make
    @make || self.class.default_make
  end

  def initialize(make=nil, year=nil)
    self.year, self.make = year, make
  end
end

ตอนนี้ขอใช้คลาสของเรา:

t = Truck.new("Honda", 2000)
t.make
# => "Honda"
t.year
# => "2000"

t = Truck.new
t.make
# => "Toyota"
t.year
# => nil

ทำให้ไม่ควรเป็นวิธีการอินสแตนซ์ มันเป็นโรงงานประเภทหนึ่งที่ควรผูกไว้กับชั้นเรียนแทนที่จะเป็นตัวอย่าง
phoet

6
@phoet ให้คำหมายถึงยี่ห้อของรถ (ในขณะที่โตโยต้า BMW ฯลฯ ) englishforums.com/English/AMakeOfCar/crcjb/post.htm ระบบการตั้งชื่อจะขึ้นอยู่กับความต้องการของผู้ใช้
Harish Shetty

8

หากคุณมีสิทธิ์เข้าถึงวิธีการมอบสิทธิ์คุณสามารถทำได้:

[20] pry(main)> class Foo
[20] pry(main)*   def self.bar
[20] pry(main)*     "foo bar"
[20] pry(main)*   end  
[20] pry(main)*   delegate :bar, to: 'self.class'
[20] pry(main)* end  
=> [:bar]
[21] pry(main)> Foo.new.bar
=> "foo bar"
[22] pry(main)> Foo.bar
=> "foo bar"

หรืออาจจะสะอาดกว่าถ้าคุณมีวิธีหรือสองวิธีที่คุณต้องการมอบสิทธิ์ให้กับคลาส & อินสแตนซ์:

[1] pry(main)> class Foo
[1] pry(main)*   module AvailableToClassAndInstance
[1] pry(main)*     def bar
[1] pry(main)*       "foo bar"
[1] pry(main)*     end  
[1] pry(main)*   end  
[1] pry(main)*   include AvailableToClassAndInstance
[1] pry(main)*   extend AvailableToClassAndInstance
[1] pry(main)* end  
=> Foo
[2] pry(main)> Foo.new.bar
=> "foo bar"
[3] pry(main)> Foo.bar
=> "foo bar"

คำเตือน:

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



5

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

ในบันทึกย่อนั้นในตัวอย่างของคุณคุณควรใช้วิธีการปกติ 'default_make' ดีกว่า:

#!/usr/bin/ruby

class Truck
    def default_make
        # Class method.
        "mac"
    end

    def initialize
        # Instance method.
        puts default_make  # gets the default via the class's method.
    end
end

myTruck = Truck.new()

วิธีการเรียนจะมีประโยชน์มากขึ้นสำหรับฟังก์ชั่นประเภทยูทิลิตี้ที่ใช้ชั้นเรียน ตัวอย่างเช่น:

#!/usr/bin/ruby

class Truck
    attr_accessor :make

    def default_make
        # Class method.
        "mac"
    end

    def self.buildTrucks(make, count)
        truckArray = []

        (1..count).each do
            truckArray << Truck.new(make)
        end

        return truckArray
    end

    def initialize(make = nil)
        if( make == nil )
            @make = default_make()
        else
            @make = make
        end
    end
end

myTrucks = Truck.buildTrucks("Yotota", 4)

myTrucks.each do |truck|
    puts truck.make
end

2
ฉันไม่เห็นด้วยที่default_makeควรเป็นวิธีการอินสแตนซ์ แม้ว่ามันจะง่ายกว่าสำหรับตัวอย่างเหล่านี้มันไม่ใช่ความหมายที่ถูกต้อง - ค่าเริ่มต้นคือผลิตภัณฑ์ของคลาสไม่ใช่วัตถุที่เป็นของคลาส
ปีเตอร์

1
@ Peter คุณอยากอธิบายว่าในแง่ง่ายกว่านี้ไหม? ฉันแค่เรียนรู้คำตอบของ Ruby และ Maha ที่สมบูรณ์แบบสำหรับฉัน
Marlen TB

1
@ MarlenT.B มองย้อนกลับไปฉันไม่แน่ใจว่าจะมีการเรียนรู้ที่นี่มากเกินไปฉันแค่เถียงว่าที่ที่ดีที่สุดในการวางวิธีการคืออะไรและฉันไม่ได้ซื้อการโต้แย้งของตัวเองอย่างแรงอีกต่อไป! :)
ปีเตอร์

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

3

อีกหนึ่ง:

class Truck
  def self.default_make
    "mac"
  end

  attr_reader :make

  private define_method :default_make, &method(:default_make)

  def initialize(make = default_make)
    @make = make
  end
end

puts Truck.new.make # => mac

1

นี่คือวิธีการที่คุณอาจใช้_classวิธีการที่ใช้เป็นself.classสถานการณ์นี้ หมายเหตุ: อย่าใช้สิ่งนี้ในรหัสการผลิตนี่คือเพื่อประโยชน์ :)

จาก: คุณสามารถเปลี่ยนรหัสในบริบทของผู้โทรใน Ruby ได้หรือไม่? และhttp://rubychallenger.blogspot.com.au/2011/07/caller-binding.html

# Rabid monkey-patch for Object
require 'continuation' if RUBY_VERSION >= '1.9.0'
class Object
  def __; eval 'self.class', caller_binding; end
  alias :_class :__
  def caller_binding
    cc = nil; count = 0
    set_trace_func lambda { |event, file, lineno, id, binding, klass|
      if count == 2
        set_trace_func nil
        cc.call binding
      elsif event == "return"
        count += 1
      end
    }
    return callcc { |cont| cc = cont }
  end
end

# Now we have awesome
def Tiger
  def roar
    # self.class.roar
    __.roar
    # or, even
    _class.roar
  end
  def self.roar
    # TODO: tigerness
  end
end

บางทีคำตอบที่ถูกต้องคือส่ง patch สำหรับ Ruby :)


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