Методы класса Ruby против методов в Eigenclasses

Являются ли методы и методы класса в eigenclass (или метаклассе) этого класса двумя способами, чтобы определить одну вещь?

В противном случае, каковы различия?

class X
  # class method
  def self.a
    "a"
  end

  # eigenclass method
  class << self
    def b
      "b"
    end
  end
end

Do X.a и X.b ведут себя по-другому в любом случае?

Я понимаю, что я могу перезаписывать или использовать методы класса alias, открывая eigenclass:

irb(main):031:0> class X; def self.a; "a"; end; end
=> nil
irb(main):032:0> class X; class << self; alias_method :b, :a; end; end
=> #<Class:X>
irb(main):033:0> X.a
=> "a"
irb(main):034:0> X.b
=> "a"
irb(main):035:0> class X; class << self; def a; "c"; end; end; end
=> nil
irb(main):036:0> X.a
=> "c"

Ответы

Ответ 1

Два метода эквивалентны. Версия "eigenclass" полезна для использования методов attr_ *, например:

class Foo
  @instances = []
  class << self;
    attr_reader :instances
  end
  def initialize
    self.class.instances << self
  end
end

2.times{ Foo.new }
p Foo.instances
#=> [#<Foo:0x2a3f020>, #<Foo:0x2a1a5c0>]

Вы также можете использовать define_singleton_method для создания методов в классе:

Foo.define_singleton_method :bim do "bam!" end

Ответ 2

В Ruby действительно нет таких вещей, как методы класса. Поскольку все является объектом Ruby (включая классы), когда вы говорите def self.class_method, вы просто действительно определяете метод singleton для экземпляра класса Class. Поэтому, чтобы ответить на ваш вопрос, говоря

class X
  def self.a
    puts "Hi"
  end

  class << self
    def b
      puts "there"
    end
  end
end

X.a # => Hi
X.b # => there

- это два способа сказать одно и то же. Оба эти метода - это только методы singeton (собственный, мета, призрак или что-то, что вы хотите назвать), определенные в экземпляре объекта Class, который в вашем примере был X. Эта тема является частью метапрограммирования, которая является интересной темой, что если вы некоторое время используете Ruby, вы должны проверить. Прагматичные программисты имеют отличную книгу о метапрограммировании, которую вы обязательно должны посмотреть, если вы заинтересованы в этой теме.

Ответ 3

Еще один некромант здесь, чтобы раскопать этот старый вопрос... Одна вещь, о которой вы, возможно, и не подозреваете, заключается в том, что маркировка метода класса как private (с использованием закрытого ключевого слова вместо :private_class_method) отличается от маркировки eigenclass метод как таковой.

class Foo
  class << self
    def baz
      puts "Eigenclass public method."
    end

    private
    def qux
      puts "Private method on eigenclass."
    end
  end
  private
  def self.bar
    puts "Private class method."
  end
end

Foo.bar
#=> Private class method.
Foo.baz
#=> Eigenclass public method.
Foo.qux
#=> NoMethodError: private method `qux' called for Foo:Class
#   from (irb)

Следующий пример будет работать так, как предполагается предыдущий:

class Foo
  class << self
    def baz
      puts "Eigen class public method."
    end

    private
    def qux
      puts "Private method on eigenclass."
    end
  end
  def bar
    puts "Private class method."
  end
  private_class_method :bar
end
Foo.bar
#=> NoMethodError: private method `bar' called for Foo:Class
#     from (irb)
Foo.baz
#=> Eigen class public method.
Foo.qux
#=> NoMethodError: private method `qux' called for Foo:Class
#     from (irb)

Ответ 4

Большинство методов экземпляра, используемых в Ruby, являются глобальными методами. Это означает, что они доступны во всех экземплярах класса, на котором они были определены. Напротив, однотонный метод реализуется на одном объекте.

Существует очевидное противоречие. Способы хранения Ruby в классах и всех методах должны быть связаны с классом. Объект, на котором определен одноэлементный метод, не является классом (это экземпляр класса). Если только классы могут хранить методы, как объект может хранить одноэлементный метод? Когда создается однопользовательский метод, Ruby автоматически создает анонимный класс для хранения этого метода. Эти анонимные классы называются метаклассами, также известными как одноэлементные классы или eigenclasses. Метод singleton связан с метаклассом, который, в свою очередь, связан с объектом, на котором был определен метод singleton.

Если в одном объекте определены несколько одноэлементных методов, все они хранятся в одном метаклассе.

class Zen
end

z1 = Zen.new
z2 = Zen.new

def z1.say_hello  # Notice that the method name is prefixed with the object name
  puts "Hello!"
end

z1.say_hello    # Output: Hello!
z2.say_hello    # Output: NoMethodError: undefined method `say_hello'…

В приведенном выше примере метод say_hello был определен внутри экземпляра z1 класса Zen, но не для экземпляра z2.

В следующем примере показан другой способ определения метода singleton с тем же результатом.

class Zen
end

z1 = Zen.new
z2 = Zen.new

class << z1
  def say_hello
    puts "Hello!"
  end
end

z1.say_hello    # Output: Hello!
z2.say_hello    # Output: NoMethodError: undefined method `say_hello'…

В приведенном выше примере класс < z1 изменяет текущий self, чтобы указать на метакласс объекта z1; то он определяет метод say_hello в метаклассе.

Оба из приведенных выше примеров служат для иллюстрации того, как работают одиночные методы. Однако существует более простой способ определения метода singleton: использование встроенного метода, называемого define_singleton_method.

class Zen
end

z1 = Zen.new
z2 = Zen.new

z1.define_singleton_method(:say_hello) { puts "Hello!" }

z1.say_hello    # Output: Hello!
z2.say_hello    # Output: NoMethodError: undefined method `say_hello'…

Ранее мы узнали, что классы также являются объектами (экземплярами встроенного класса Class). Мы также узнали о методах класса. Методы класса - это не что иное, как одиночные методы, связанные с объектом класса.

Еще один пример:

class Zabuton
  class << self
    def stuff
      puts "Stuffing zabuton…"
    end
  end
end

Все объекты могут иметь метаклассы. Это означает, что классы также могут иметь метаклассы. В приведенном выше примере класс < self изменяет self, поэтому он указывает на метакласс класса Zabuton. Когда метод определен без явного приемника (класс/объект, на котором будет определяться метод), он неявно определяется в текущей области, то есть текущее значение self. Следовательно, метод stuff определяется в метаклассе класса Zabuton. Вышеприведенный пример - это еще один способ определения метода класса.

Подробнее на этот пост о классах Ruby.