Методы класса 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.