En Ruby, comment appelez-vous une méthode de classe à partir de l'une des instances de cette classe? Dis que j'ai
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 ligne Truck.default_make
récupère la valeur par défaut. Mais y a-t-il moyen de dire cela sans mentionner Truck
? Il semble qu'il devrait y en avoir.
Plutôt que de faire référence au nom littéral de la classe, dans une méthode d'instance, vous pouvez simplement appeler 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
Les sorties:
Méthode de classe: Foo Méthode d'instance: Foo
Utiliser self.class.blah
n'est PAS la même chose que d'utiliser ClassName.blah
en matière d'héritage.
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"
Pour accéder à une méthode de classe dans une méthode d'instance, procédez comme suit:
self.class.default_make
Voici une solution alternative à votre problème:
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
Maintenant, utilisons notre classe:
t = Truck.new("Honda", 2000)
t.make
# => "Honda"
t.year
# => "2000"
t = Truck.new
t.make
# => "Toyota"
t.year
# => nil
self.class.default_make
Si vous avez accès à la méthode déléguée, vous pouvez le faire:
[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"
Sinon, et probablement plus propre si vous avez plus d'une méthode ou deux que vous souhaitez déléguer à class & instance:
[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 mot d'avertissement:
Ne faites pas que de façon aléatoire delegate
tout ce qui ne change pas d'état en classe et en instance, car vous allez commencer à rencontrer d'étranges problèmes de conflit de noms. Faites-le avec parcimonie et seulement après avoir vérifié rien d'autre n'est écrasé.
Vous le faites de la bonne façon. Les méthodes de classe (similaires aux méthodes "statiques" en C++ ou Java) ne font pas partie de l'instance, elles doivent donc être référencées directement.
Sur cette note, dans votre exemple, vous feriez mieux de faire de 'default_make' une méthode régulière:
#!/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()
Les méthodes de classe sont plus utiles pour les fonctions de type utilitaire qui utilisent la classe. Par exemple:
#!/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
Un de plus:
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
Voici une approche sur la façon dont vous pourriez implémenter une méthode _class
qui fonctionne comme self.class
dans cette situation. Note: N'utilisez pas ceci dans le code de production, c'est pour votre intérêt :)
De: Pouvez-vous évaluer le code dans le contexte d'un appelant en Ruby? et aussi 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
Peut-être que la bonne réponse est de soumettre un correctif pour Ruby :)