Ruby: Wywołanie metody klasy z instancji


347

W Ruby, jak wywołać metodę klasową z jednej z instancji tej klasy? Powiedz, że mam

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

linia Truck.default_makepobiera wartość domyślną. Ale czy można to powiedzieć bez wspominania Truck? Wygląda na to, że powinno być.

Odpowiedzi:


563

Zamiast odwoływać się do dosłownej nazwy klasy, w metodzie instancji można po prostu wywołać 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

Wyjścia:

Metoda klasowa: Foo
Metoda instancji: Foo

7
Chciałbym zobaczyć skrót w Rubim, aby wywołać metodę klasy z instancji. tj .:> some_class_method zamiast self.class.some_class_method
phoet

7
choć jest to właściwa odpowiedź, szkoda, że ​​„self.class” jest bardziej typowy i trudniejszy do odczytania niż nazwa klasy „Truck”. no cóż ...
Matt Connolly,

22
@MattConnolly, jest względny, jeśli twoja nazwa klasy jest SalesforceSyncJobkrótsza;)
narysuj

29
@MattConnolly, również użycie self.classeliminuje potrzebę wyszukiwania / zastępowania, jeśli zdarzy się zmiana nazwy klasy.
Gus Shortz

8
@GusShortz true. Ponadto self.class działa lepiej, jeśli istnieje podklasa.
Matt Connolly,

183

Używanie self.class.blahNIE jest tym samym, co używanie, ClassName.blahjeśli chodzi o dziedziczenie.

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
Wydaje się, że jest to raczej odpowiedź na zaakceptowaną odpowiedź niż odpowiedź na pytanie.
zhon

16
@zohn - prawda, ale nadal jest to przydatny kontekst przy rozważaniu, czego użyć.
Matt Sanders,

1
@MattSanders po prostu użyj komentarza w takich przypadkach.
nandilugio

1
@hlcs self.classjest poprawne, aby zachować dziedzictwo. mimo że make1()jest zdefiniowany w Truck, odwołuje się do BigTruckmetody klasy.
Kaiser Shahid

14

Aby uzyskać dostęp do metody klasy w metodzie instancji, wykonaj następujące czynności:

self.class.default_make

Oto alternatywne rozwiązanie problemu:

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

Teraz skorzystajmy z naszej klasy:

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

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

make nie powinno być metodą instancji. to raczej rodzaj fabryki, która powinna być powiązana z klasą, a nie z instancją
phoet

6
@ phoet Słowo make oznacza markę samochodu (jak w Toyocie, BMW itp.) englishforums.com/English/AMakeOfCar/crcjb/post.htm . Nomenklatura oparta jest na wymaganiach użytkownika
Harish Shetty

8

Jeśli masz dostęp do metody delegowania, możesz to zrobić:

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

Alternatywnie i prawdopodobnie czystszy, jeśli masz więcej niż jedną lub dwie metody, które chcesz przekazać do klasy i instancji:

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

Słowo ostrzeżenia:

Nie tylko losowo delegatewszystko, co nie zmienia stanu na klasę i instancję, ponieważ zaczniesz napotykać dziwne problemy ze zderzeniami nazw. Zrób to oszczędnie i dopiero po sprawdzeniu nic innego nie zostanie zmiażdżone.



5

Robisz to we właściwy sposób. Metody klasowe (podobne do metod „statycznych” w C ++ lub Javie) nie są częścią instancji, więc należy się do nich bezpośrednio odwoływać.

W tym przypadku lepiej byłoby w twoim przykładzie uczynić „default_make” regularną metodą:

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

Metody klasowe są bardziej przydatne w przypadku funkcji typu narzędziowego, które wykorzystują klasę. Na przykład:

#!/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
Nie zgadzam się, że default_makepowinna to być metoda instancji. Nawet jeśli dla tych przykładów jest to prostsze, nie jest to właściwa semantyka - domyślnie jest produktem klasy, a nie obiektami należącymi do klasy.
Peter

1
@Peter chciałbyś wyjaśnić to prościej? Właśnie się uczę, że odpowiedzi Ruby i Mahy wydają mi się idealne.
Marlen TB

1
@ MarlenT.B. patrząc wstecz, nie jestem pewien, czy jest tu zbyt wiele do nauczenia się - spierałem się tylko o to, gdzie najlepiej umieścić tę metodę, i nie kupuję już własnego argumentu tak mocno! :)
Peter

2
Ja też się nie zgadzam. To, czy coś jest metodą klasową, nie ma nic wspólnego z „narzędziem”. Chodzi o to, czy metoda koncepcyjnie dotyczy klasy, czy obiektu tej klasy. Na przykład każda ciężarówka ma inny numer seryjny, więc numer_seryjny jest metodą instancji (z odpowiednią zmienną instancji). Z drugiej strony typ pojazdu (który zwraca „ciężarówka”) powinien być metodą klasową, ponieważ jest to właściwość wszystkich ciężarówek, a nie konkretnej ciężarówki
vish

3

Jeszcze jeden:

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

Oto podejście, w jaki sposób możesz zaimplementować _classmetodę, która działa jak self.classw tej sytuacji. Uwaga: nie używaj tego w kodzie produkcyjnym, to ze względu na zainteresowanie :)

Od: Czy możesz ewaluować kod w kontekście dzwoniącego w Ruby? a także 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

Może właściwą odpowiedzią jest przesłanie łatki dla Ruby :)


-6

Podobnie twoje pytanie, możesz użyć:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.