2011-01-11 11 views
137

Capisco il concetto di some_instance.send ma sto cercando di capire perché è possibile chiamare in entrambi i modi. Il Ruby Koans implica che ci sia qualche motivo oltre a fornire molti modi diversi di fare la stessa cosa. Ecco i due esempi di utilizzo:Ruby send vs __send__

class Foo 
    def bar? 
    true 
    end 
end 

foo = Foo.new 
foo.send(:bar?) 
foo.__send__(:bar?) 

Qualcuno ha qualche idea al riguardo?

risposta

213

Alcune classi (ad esempio la classe socket della libreria standard) definiscono il proprio metodo send che non ha nulla a che fare con Object#send. Quindi se vuoi lavorare con oggetti di qualsiasi classe, devi usare __send__ per essere al sicuro.

Ora questo lascia la domanda, perché c'è send e non solo __send__. Se ci fossero solo __send__ il nome send potrebbe essere utilizzato da altre classi senza alcuna confusione. Il motivo è che prima esisteva send e che il nome send poteva essere utilizzato anche in altri contesti, quindi è stato aggiunto __send__ (è la stessa cosa che è successo con id e object_id).

+7

Inoltre, [BasicObject] (http://ruby-doc.org/core/BasicObject.html) (introdotto in Ruby 1.9) ha solo '__send__', non' send'. –

+0

Buona risposta.Potrebbe essere ancora meglio se menzionasse 'public_send', che è spesso preferibile a 'send' comunque. –

29

Se davvero bisogno send a comportarsi come si farebbe normalmente, è necessario utilizzare __send__, perché non sarà (non dovrebbe) essere sovrascritto. L'utilizzo di __send__ è particolarmente utile in metaprogrammazione, quando non si conoscono i metodi definiti dalla classe manipolata. Potrebbe avere sostituito send.

Guarda:

class Foo 
    def bar? 
    true 
    end 

    def send(*args) 
    false 
    end 
end 

foo = Foo.new 
foo.send(:bar?) 
# => false 
foo.__send__(:bar?) 
# => true 

Se si ignora __send__, Ruby emetterà un avvertimento:

avvertimento: la ridefinizione `__send__' may causare seri problemi

alcuni casi in cui sarebbe utile sovrascrivere send sarebbe dove quel nome è appropriato, come il messaggio pas cantare, prendere lezioni, ecc.

9

__send__ esiste quindi non può essere sovrascritto per errore.

Per quanto riguarda il motivo per cui esiste send: Non posso parlare per chiunque altro, ma object.send(:method_name, *parameters) sembra più bello di object.__send__(:method_name, *parameters), per cui uso send meno che non ho bisogno da usare __send__.

5

A parte ciò che gli altri già detto, e che cosa si riduce a dire che send e __send__ sono due alias dello stesso metodo, si potrebbe essere interessato al terzo, possibilita somwhat diverso, che è public_send. Esempio:

A, B, C = Module.new, Module.new, Module.new 
B.include A #=> error -- private method 
B.send :include, A #=> bypasses the method's privacy 
C.public_send :include, A #=> does not bypass privacy 

Aggiornamento: Dal Rubino 2.1, Module#include e Module#extend metodi diventano pubblici, quindi l'esempio di cui sopra non funzionerebbe più.

+0

buona conoscenza, grazie! – jaydel