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
Метод экземпляра: Foo
Марк Рушаков
источник
7
Я хотел бы увидеть некоторые ярлыки в ruby ​​для вызова метода класса из экземпляра. ie:> some_class_method вместо self.class.some_class_method
phoet
7
хотя это правильный ответ, обидно, что «self.class» более типичен и менее удобен для чтения, чем имя класса «Truck». да ладно ....
Мэтт Коннолли
22
@MattConnolly, это относительно, если ваше имя класса, SalesforceSyncJobто оно короче;)
drewish
29
@MattConnolly, также использование self.classисключает необходимость поиска / замены, если вам случится переименовать класс.
Гас Шортц
8
@GusShortz правда. Кроме того, self.class работает лучше, если есть подкласс.
Мэтт Коннолли
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
Похоже, что это ответ на принятый ответ, а не ответ на вопрос.
жон
16
@zohn - правда, но это все еще полезный контекст при рассмотрении того, что использовать.
Мэтт Сандерс
1
@MattSanders просто используйте комментарий в этих случаях.
nandilugio
1
@hlcs self.classявляется правильным для сохранения наследства. хотя make1()он определен в Truck, он ссылается BigTruckна метод класса.
Кайзер Шахид
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
Хариш Шетти
источник
make не должен быть методом экземпляра. это скорее своего рода фабрика, которая должна быть привязана к классу, а не к экземпляру
phoet
6
@phoet Слово «make» обозначает марку автомобиля (как в Toyota, BMW и т. д.) englishforums.com/English/AMakeOfCar/crcjb/post.htm . Номенклатура основана на требованиях пользователя
Хариш Шетти,
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все, что не меняет состояние на класс и экземпляр, потому что вы начнете сталкиваться со странными проблемами столкновения имен. Делайте это экономно и только после того, как вы проверили, ничего больше не раздавлено.

bbozo
источник
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
@ Питер, не могли бы вы объяснить это проще? Я только изучаю ответы Руби и Маха, которые кажутся мне идеальными.
Марлен Т.Б.
1
@ MarlenT.B. оглядываясь назад, я не уверен, что здесь есть чему поучиться - я только спорил о том, где лучше всего разместить метод, и я больше не покупаю свои собственные аргументы! :)
Питер
2
Я тоже не согласен. Является ли метод класса классом, не имеет ничего общего с «утилитой». Речь идет о том, является ли метод концептуально применимым к классу или объекту этого класса. Например, у каждого грузовика есть свой серийный номер, поэтому serial_number - это метод экземпляра (с соответствующей переменной экземпляра). На другом vehicle_type (который возвращает «грузовик») должен быть метод класса, потому что это свойство всех грузовиков, а не конкретного грузовика
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

Возможно, правильный ответ - представить патч для Ruby :)

captainpete
источник
-6

Подобный ваш вопрос, вы можете использовать:

class Truck
  def default_make
    # Do something
  end

  def initialize
    super
    self.default_make
  end
end
Даниил
источник