Ruby: Aufruf der Klassenmethode von der Instanz

347

Wie ruft man in Ruby eine Klassenmethode aus einer der Instanzen dieser Klasse auf? Sag ich habe

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

Die Zeile Truck.default_makeruft die Standardeinstellung ab. Aber gibt es eine Möglichkeit, dies zu sagen, ohne es zu erwähnen Truck? Es scheint so, als ob es das geben sollte.

Peter
quelle

Antworten:

563

Anstatt auf den Literalnamen der Klasse zu verweisen, können Sie innerhalb einer Instanzmethode einfach aufrufen 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

Ausgänge:

Klassenmethode: Foo
Instanzmethode: Foo
Mark Rushakoff
quelle
7
Ich würde gerne eine Verknüpfung in Ruby sehen, um eine Klassenmethode aus einer Instanz aufzurufen. dh:> some_class_method anstelle von self.class.some_class_method
phoet
7
Obwohl dies die richtige Antwort ist, ist es eine Schande, dass "self.class" mehr tippt und weniger leicht zu lesen ist als der Klassenname "Truck". na ja ....
Matt Connolly
22
@ MattConnolly, es ist relativ, wenn Ihr Klassenname ist, SalesforceSyncJobdann ist es kürzer;)
Zeichnen
29
@MattConnolly, auch die Verwendung macht self.classdas Suchen / Ersetzen überflüssig, wenn Sie die Klasse umbenennen.
Gus Shortz
8
@ GusShortz wahr. Außerdem funktioniert self.class besser, wenn es eine Unterklasse gibt.
Matt Connolly
183

Verwenden self.class.blahist NICHT dasselbe wie Verwenden, ClassName.blahwenn es um Vererbung geht.

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" 
Mark B.
quelle
58
Dies scheint eher eine Antwort auf die akzeptierte Antwort als eine Antwort auf die Frage zu sein.
Zhon
16
@zohn - stimmt, aber dies ist immer noch ein nützlicher Kontext, wenn Sie überlegen, was Sie verwenden möchten.
Matt Sanders
1
@MattSanders verwenden in diesen Fällen nur einen Kommentar.
Nandilugio
1
@hlcs self.classist korrekt, um die Vererbung beizubehalten . Obwohl make1()in definiert Truck, verweist es auf die BigTruckKlassenmethode.
Kaiser Shahid
Class_name.method_name funktioniert perfekt
Houda M
14

Gehen Sie wie folgt vor, um auf eine Klassenmethode innerhalb einer Instanzmethode zuzugreifen:

self.class.default_make

Hier ist eine alternative Lösung für Ihr Problem:

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

Verwenden wir jetzt unsere Klasse:

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

t = Truck.new
t.make
# => "Toyota"
t.year
# => nil
Harish Shetty
quelle
make sollte keine Instanzmethode sein. Es ist eher eine Art Fabrik, die eher an die Klasse als an eine Instanz gebunden sein sollte
Phoet
6
@phoet Das Wort make bezeichnet die Marke eines Autos (wie bei Toyota, BMW usw.). englishforums.com/English/AMakeOfCar/crcjb/post.htm . Die Nomenklatur basiert auf den Anforderungen des Benutzers
Harish Shetty
8

Wenn Sie Zugriff auf die Delegate-Methode haben, können Sie Folgendes tun:

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

Alternativ und wahrscheinlich sauberer, wenn Sie mehr als eine oder zwei Methoden haben, die Sie an class & instance delegieren möchten:

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

Ein Wort der Vorsicht:

Nicht nur zufällig delegatealles, was den Status nicht in Klasse und Instanz ändert, da Sie auf seltsame Probleme mit Namenskonflikten stoßen. Tun Sie dies sparsam und erst nachdem Sie überprüft haben, wird nichts anderes gequetscht.

bbozo
quelle
6
self.class.default_make
Yfeldblum
quelle
5

Du machst es richtig. Klassenmethoden (ähnlich wie 'statische' Methoden in C ++ oder Java) sind nicht Teil der Instanz, daher müssen sie direkt referenziert werden.

In diesem Sinne ist es in Ihrem Beispiel besser, wenn Sie 'default_make' zu einer regulären Methode machen:

#!/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()

Klassenmethoden sind nützlicher für Dienstprogrammfunktionen, die die Klasse verwenden. Zum Beispiel:

#!/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
Jason Machacek
quelle
2
Ich bin nicht der Meinung, dass default_makedies eine Instanzmethode sein sollte. Auch wenn es für diese Beispiele einfacher ist, ist es nicht die richtige Semantik - der Standard ist ein Produkt der Klasse, keine Objekte, die zur Klasse gehören.
Peter
1
@Peter möchten Sie das in einfacheren Worten erklären? Ich lerne gerade Ruby und Mahas Antworten scheinen mir perfekt zu sein.
Marlen TB
1
@ MarlenT.B. Rückblickend bin ich mir nicht sicher, ob es hier zu viel zu lernen gibt - ich habe nur darüber gestritten, wo der beste Ort für die Methode war, und ich kaufe mein eigenes Argument nicht mehr so ​​stark! :)
Peter
2
Ich bin auch anderer Meinung. Ob etwas eine Klassenmethode ist, hat nichts mit "Dienstprogramm" zu tun. Es geht darum, ob die Methode konzeptionell für die Klasse oder ein Objekt dieser Klasse gilt. Beispielsweise hat jeder LKW eine andere Seriennummer, sodass serial_number eine Instanzmethode ist (mit der entsprechenden Instanzvariablen). Auf der anderen Seite sollte der Fahrzeugtyp (der "LKW" zurückgibt) eine Klassenmethode sein, da dies eine Eigenschaft aller LKWs ist, nicht eines bestimmten LKW
vish
3

Einer noch:

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
Alexey
quelle
1

Hier ist ein Ansatz, wie Sie eine _classMethode implementieren können , die in self.classdieser Situation funktioniert . Hinweis: Verwenden Sie dies nicht im Produktionscode, dies ist aus Interesse :)

Von: Können Sie Code im Kontext eines Anrufers in Ruby bewerten? und auch 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

Vielleicht ist die richtige Antwort, einen Patch für Ruby einzureichen :)

Captainpete
quelle
-6

Ähnlich wie bei Ihrer Frage könnten Sie Folgendes verwenden:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Daniel
quelle