Ruby: chiamata del metodo di classe dall'istanza


347

In Ruby, come si chiama un metodo di classe da una delle istanze di quella classe? Di 'che ho

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

la riga Truck.default_makerecupera il valore predefinito. Ma c'è un modo di dirlo senza menzionare Truck? Sembra che ci dovrebbe essere.

Risposte:


563

Invece di fare riferimento al nome letterale della classe, all'interno di un metodo di istanza puoi semplicemente chiamare 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

Uscite:

Metodo di classe: Foo
Metodo di istanza: Foo

7
vorrei vedere qualche scorciatoia in ruby ​​per chiamare un metodo di classe da un'istanza. vale a dire:> some_class_method invece di self.class.some_class_method
phoet

7
mentre questa è la risposta giusta, è un peccato che "self.class" sia più digitato e meno facile da leggere rispetto al nome della classe "Truck". vabbè ....
Matt Connolly,

22
@MattConnolly, è relativo, se il nome della tua classe è SalesforceSyncJoballora è più breve;)
disegnato il

29
@MattConnolly, inoltre, l'utilizzo self.classelimina la necessità di cercare / sostituire se ti capita di rinominare la classe.
Gus Shortz,

8
@GusShortz true. Inoltre, self.class funziona meglio se esiste una sottoclasse.
Matt Connolly,

183

L'uso self.class.blahNON è uguale all'utilizzo ClassName.blahquando si tratta di ereditarietà.

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
Questa sembra essere una risposta alla risposta accettata piuttosto che una risposta alla domanda.
Zhon,

16
@zohn - vero, ma questo è ancora un contesto utile quando si considera cosa usare.
Matt Sanders,

1
@MattSanders usa solo un commento in questi casi.
nandilugio,

1
@hlcs self.classè corretto per preservare l'eredità. anche se make1()è definito in Truck, è il BigTruckmetodo di classe di riferimento .
Kaiser Shahid,

Class_name.method_name funziona perfettamente
Houda M

14

Per accedere a un metodo di classe all'interno di un metodo di istanza, procedere come segue:

self.class.default_make

Ecco una soluzione alternativa per il tuo problema:

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

Ora usiamo la nostra classe:

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

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

make non dovrebbe essere un metodo di istanza. è più una specie di fabbrica, che dovrebbe essere legata alla classe piuttosto che a un'istanza
phoet,

6
@phoet La parola "make" indica la marca di un'auto (come in Toyota, BMW, ecc.) englishforums.com/English/AMakeOfCar/crcjb/post.htm . La nomenclatura si basa sulle esigenze dell'utente
Harish Shetty,

8

Se hai accesso al metodo delegato puoi farlo:

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

In alternativa, e probabilmente più pulito se hai più di un metodo o due che vuoi delegare alla classe e all'istanza:

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

Un avvertimento:

Non casualmente delegatetutto ciò che non cambia stato in classe e istanza perché inizierai a imbatterti in strani problemi di scontro tra nomi. Fallo con parsimonia e solo dopo aver verificato che nient'altro viene schiacciato.



5

Lo stai facendo nel modo giusto. I metodi di classe (simili ai metodi "statici" in C ++ o Java) non fanno parte dell'istanza, quindi devono essere referenziati direttamente.

In quella nota, nel tuo esempio saresti meglio servito a rendere 'default_make' un metodo regolare:

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

I metodi di classe sono più utili per le funzioni di tipo utility che utilizzano la classe. Per esempio:

#!/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
Non sono d'accordo che default_makedovrebbe essere un metodo di istanza. Anche se è più semplice per questi esempi, non è la semantica giusta: il valore predefinito è un prodotto della classe, non oggetti che appartengono alla classe.
Peter,

1
@Peter ti dispiacerebbe spiegarlo in termini più semplici? Sto solo imparando Ruby e le risposte di Maha mi sembrano perfette.
Marlen TB,

1
@ MarlenT.B. guardando indietro non sono sicuro che ci sia troppo da imparare qui - stavo solo discutendo su dove fosse il posto migliore dove mettere il metodo, e non compro più il mio argomento con forza! :)
Peter,

2
Sono anche in disaccordo. Il fatto che qualcosa sia un metodo di classe non ha nulla a che fare con "utility". Si tratta se il metodo si applica concettualmente alla classe o ad un oggetto di quella classe. Ad esempio, ogni camion ha un numero seriale diverso, quindi serial_number è un metodo di istanza (con la corrispondente variabile di istanza). Sull'altro tipo di veicolo (che restituisce "camion") dovrebbe esserci un metodo di classe perché è una proprietà di tutti i camion, non di un camion particolare
vish

3

Ancora uno:

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

Ecco un approccio su come è possibile implementare un _classmetodo che funziona come self.classper questa situazione. Nota: non utilizzare questo nel codice di produzione, questo è per interesse :)

Da: puoi valutare il codice nel contesto di un chiamante in Ruby? e anche 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

Forse la risposta giusta è inviare una patch per Ruby :)


-6

Simile alla tua domanda, puoi usare:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.