Я застреваю. Я пытаюсь динамично определить метод класса, и я не могу перенести голову вокруг рубиновой модели метакласса. Рассмотрите следующий класс:
class Example
def self.meta; (class << self; self; end); end
def self.class_instance; self; end
end
Example.class_instance.class # => Class
Example.meta.class # => Class
Example.class_instance == Example # => true
Example.class_instance == Example.meta # => false
Очевидно, оба метода возвращают экземпляр Класса. Но эти два экземпляра не являются тем же. У них также есть различные предки:
Example.meta.ancestors # => [Class, Module, Object, Kernel]
Example.class_instance.ancestors # => [Example, Object, Kernel]
Какой смысл того, чтобы иметь значение между метаклассом и экземпляром класса?
Я выяснил, что я могу send :define_method
к метаклассу для динамичного определения метода но если я пытаюсь отправить его в экземпляр класса, это не будет работать. По крайней мере, я мог решить свою проблему, но я все еще хочу понять, почему она прокладывает себе путь.
Обновление 15 марта 2010 13:40
Следующие корректные предположения.
Я думаю, что это начинает иметь смысл мне. Это, конечно, ограничило бы Ваши возможности, если сам в методе класса укажет на собственный класс. Раз так не было бы возможно определить метод экземпляра из метода класса. Это корректно?
Определить одноэлементный метод динамически просто, если вы используете instance_eval
:
Example.instance_eval{ def square(n); n*n; end }
Example.square(2) #=> 4
# you can pass instance_eval a string as well.
Example.instance_eval "def multiply(x,y); x*y; end"
Example.multiply(3,9) #=> 27
Что касается разницы выше, вы путаете две вещи :
Определенный вами мета-класс - это то, что в сообществе Ruby называется классом сингелтона или собственным классом . Этот одноэлементный класс - это класс, к которому вы можете добавлять методы класса (одноэлементного).
Что касается экземпляра класса, который вы пытаетесь определить с помощью метода class_instance
, это не что иное, как сам класс, чтобы доказать это, просто попробуйте добавить метод экземпляра к классу Пример
и проверьте, возвращает ли определенный вами метод class_instance
сам класс Example
, проверив существование этого метода:
class Example
def self.meta; (class << self; self; end); end
def self.class_instance; self; end
def hey; puts hey; end
end
Example.class_instance.instance_methods(false) #=> ['hey']
В любом случае, чтобы суммировать его для вас, когда вы хотите добавить методы класса, просто добавьте их в этот мета-класс. Поскольку метод class_instance
бесполезен, просто удалите его.
В любом случае, я предлагаю вам прочитать этот пост , чтобы понять некоторые концепции системы отражения Ruby.
ОБНОВЛЕНИЕ
Предлагаю вам прочитать этот хороший пост: Развлечение с Ruby instance_eval и class_eval ,
К сожалению, class_eval
и instance_eval
являются сбивает с толку, потому что они каким-то образом работают против их именования!
Use ClassName.instance_eval to define class methods.
Use ClassName.class_eval to define instance methods.
Теперь отвечу на ваши предположения:
Если у меня есть метод экземпляра, который вызывает self.instance_eval и определяет метод , это повлияет только на конкретный экземпляр этот класс.
да:
class Foo
def assumption1()
self.instance_eval("def test_assumption_1; puts 'works'; end")
end
end
f1 = Foo.new
f1.assumption1
f1.methods(false) #=> ["test_assumption_1"]
f2 = Foo.new.methods(false) #=> []
Если у меня есть метод экземпляра, который вызывает self.class.instance_eval (который будет таким же, как вызов class_eval) и определяет метод он повлияет на все экземпляры этого конкретного класса, в результате чего будет создан новый метод экземпляра .
no instance_eval
в этом контексте будет определять одноэлементные методы (не экземпляры) для самого класса:
class Foo
def assumption2()
self.class.instance_eval("def test_assumption_2; puts 'works'; end")
end
end
f3 = Foo.new
f3.assumption2
f3.methods(false) #=> []
Foo.singleton_methods(false) #=> ["test_assumption_2"]
Чтобы это работало, замените instance_eval
на class_eval
] выше.
Если у меня есть метод класса, который вызывает instance_eval и определяет метод, он приведет к созданию нового метода экземпляра для всех экземпляров.
Нет:
class Foo
instance_eval do
def assumption3()
puts 'works'
end
end
end
Foo.instance_methods(false) #=> []
Foo.singleton_methods(false) #=> ["assumption_3"]
Это будет делать одноэлементные методы, а не методы экземпляра. Чтобы это работало, замените instance_eval
на class_eval
выше.
Если у меня есть метод класса, который вызывает instance_eval в мета / собственном классе и определяет метод, это приведет к методу класса.
ну нет, это сделает такой сложный материал, как добавление одноэлементного метода к одноэлементному классу, я не думаю, что это будет иметь какое-то практическое применение.
Если вы определяете метод на классе, он может быть вызван на его объектах. Это метод экземпляра.
class Example
end
Example.send :define_method, :foo do
puts "foo"
end
Example.new.foo
#=> "foo"
Если вы определяете метод на метаклассе, он может быть вызван на классе. Это похоже на концепцию метода класса или статического метода в других языках.
class Example
def self.metaclass
class << self
self
end
end
end
Example.metaclass.send :define_method, :bar do
puts "bar"
end
Example.bar
#=> "bar"
Причина существования метаклассов в том, что вы можете сделать это в Ruby:
str = "hello"
class << str
def output
puts self
end
end
str.output
#=> "hello"
"hi".output
# NoMethodError
Как вы видите, мы определили метод, который доступен только для одного экземпляра String. То, для чего мы определили этот метод, называется метакласс. В цепочке поиска метода перед поиском класса объекта сначала обращаются к метаклассу.
Если мы заменим объект типа String
на объект типа Class
, вы можете представить, почему это означает, что мы определяем метод только для конкретного класса, а не для всех классов.
Различия между текущим контекстом и self
очень тонкие, вы можете прочитать подробнее, если вам интересно.