2016-01-12 3 views
3

Это наблюдение вопрос How to determine the class a method was defined in? (надеюсь, вы не возражаете сходство)Получение экземпляра метода внутри метода в Ruby,

Учитывая иерархию классов, может метод извлечения собственной Method пример?

class A 
    def foo 
    puts "A#foo: `I am #{method(__method__)}'" 
    end 
end 

class B < A 
    def foo 
    puts "B#foo: `I am #{method(__method__)}'" 
    super 
    end 
end 

A.new.foo 
# A#foo: `I am #<Method: A#foo>' 

B.new.foo 
# B#foo: `I am #<Method: B#foo>' 
# A#foo: `I am #<Method: B#foo>' # <- A#foo retrieves B#foo 

Так что вместо B.new.foo печатает

# B#foo: `I am #<Method: B#foo>' 
# A#foo: `I am #<Method: A#foo>' # <- this is what I want 

В предыдущем вопросе, Jörg W Mittag suspected, что получение класс метод был определен в может привести к нарушению объектно-ориентированные парадигмы. Означает ли это и здесь?

Не должен ли метод «знать себя»?

+0

Возможно, чит будет каким-то образом сделать эту интернированную строку, а затем вызвать ее один раз из исходного местоположения. – sawa

+1

@sawa печать экземпляра метода не является точкой, хотя замороженная строка, вероятно, может работать в этом случае ;-) – Stefan

ответ

3

Я нашел method, что именно так.

class A 
    def foo 
    puts "A#foo: `I am #{method(__method__).super_method || method(__method__)}'" 
    end 
end 

Я действительно восхищаюсь разработчиками ядра Matz и Ruby. Существование такого метода означает, что они имели в виду такую ​​ситуацию и думали о том, что с ним делать.

+3

Ruby 2.2 + только. Неверный вывод, как и ожидалось, никогда не печатает 'B # foo'. Кроме того, не работает - если бы у нас был класс A

+0

@WandMaker Вероятно, определение класса «А» было основным моментом Стефана. Поэтому я удалил определение для 'B'. Если это необходимо, исходный код Стефана может быть использован для этой части – sawa

+0

@WandMaker Что касается вашей точки с 'C', это не проблема. Мы можем определить метод двумя разными способами в зависимости от необходимости. Если мы хотим вернуть имя лексического метода, мы можем использовать то, что я дал в ответ. Если мы хотим, чтобы имя метода зависело от получателя, мы можем использовать исходный код Стефана. – sawa

2

Основываясь на ответе How to determine the class a method was defined in? и ответ @ Саввы относительно super_method, вы можете использовать:

def meth(m, clazz) 
    while (m && m.owner != clazz) do 
     m = m.super_method 
    end 
    return m 
end 

class A 
    def foo 
    puts "A#foo: `I am #{meth(method(__method__), Module.nesting.first)}'" 
    end 
end 

class B < A 
    def foo 
    puts "B#foo: `I am #{meth(method(__method__), Module.nesting.first)}'" 
    super 
    end 
end 


B.new.foo 
# B#foo: `I am #<Method: B#foo>' 
# A#foo: `I am #<Method: A#foo>' 

Идея здесь в том, что, так как мы знаем, класс/модуль, где метод, определенный Module.nesting.first, мы принимаем текущий объект метода найден по method(__method__) и итерации по цепочке super, чтобы найти этот экземпляр метода, владелец которого тот же, что и класс/модуль, который определил метод.

+0

Итак, вы согласны с тем, что использование' super_method' находится на правильном пути? – sawa

+0

@sawa Yea, это, безусловно, помогает –

+0

Перемещение цепи 'super_method' до текущего владельца метода - интересный подход. Он, безусловно, исправляет «родительскую проблему» с помощью 'C'. – Stefan

Смежные вопросы