Je comprends le concept de some_instance.send
mais j'essaie de comprendre pourquoi vous pouvez appeler cela dans les deux sens. Le Ruby Koans implique qu'il y a une raison au-delà de fournir de nombreuses façons différentes de faire la même chose. Voici les deux exemples d'utilisation:
class Foo
def bar?
true
end
end
foo = Foo.new
foo.send(:bar?)
foo.__send__(:bar?)
Quelqu'un a une idée à ce sujet?
Certaines classes (par exemple la classe socket de la bibliothèque standard) définissent leur propre méthode send
qui n'a rien à voir avec Object#send
. Donc, si vous voulez travailler avec des objets de n'importe quelle classe, vous devez utiliser __send__
être en sécurité.
Maintenant cela laisse la question, pourquoi il y a send
et pas seulement __send__
. S'il n'y avait que __send__
le nom send
pourrait être utilisé par d'autres classes sans aucune confusion. La raison en est que send
a existé en premier et seulement plus tard, il a été réalisé que le nom send
pouvait également être utilisé utilement dans d'autres contextes, donc __send__
a été ajouté (c'est la même chose qui s'est produite avec id
et object_id
au fait).
Si vous vraiment avez besoin de send
pour vous comporter comme il le ferait normalement, vous devez utiliser __send__
, car il ne sera pas (il ne devrait pas) être annulé. En utilisant __send__
est particulièrement utile dans la métaprogrammation, lorsque vous ne savez pas quelles méthodes la classe manipulée définit. Il aurait pu remplacer send
.
Regarder:
class Foo
def bar?
true
end
def send(*args)
false
end
end
foo = Foo.new
foo.send(:bar?)
# => false
foo.__send__(:bar?)
# => true
Si vous remplacez __send__
, Ruby émettra un avertissement:
avertissement: la redéfinition de "__send__" peut entraîner de graves problèmes
Certains cas où il serait utile de remplacer send
seraient où ce nom est approprié, comme le passage de messages, les classes de socket, etc.
__send__
Existe donc il ne peut pas être écrasé par accident.
Quant à savoir pourquoi send
existe: je ne peux parler pour personne d'autre, mais object.send(:method_name, *parameters)
est plus joli que object.__send__(:method_name, *parameters)
, donc j'utilise send
sauf si je besoin pour utiliser __send__
.
Mis à part ce que les autres vous ont déjà dit et ce qui revient à dire que send
et __send__
sont deux alias de la même méthode, vous pourriez être intéressé par la troisième possibilité, quelque peu différente, qui est public_send
. Exemple:
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
Mise à jour: depuis Ruby 2.1, Module#include
et Module#extend
les méthodes deviennent publiques, donc l'exemple ci-dessus ne fonctionnerait plus.
La principale différence entre envoyer, __send__
, et public_send est comme suit.
__send__
sont techniquement les mêmes que celles utilisées pour appeler la méthode Object, mais la principale différence est que vous pouvez remplacer la méthode d'envoi sans aucun avertissement et lorsque vous remplacez __send__
puis il y a un message d'avertissementavertissement: redéfinir
__send__
peut causer de graves problèmes
En effet, pour éviter les conflits, en particulier dans les gemmes ou les bibliothèques lorsque le contexte où il sera utilisé est inconnu, utilisez toujours __send__
au lieu d'envoyer.
__send__
) et public_send est que send/__send__
peut appeler les méthodes privées d'un objet, et public_send ne peut pas.class Foo
def __send__(*args, &block)
"__send__"
end
def send(*args)
"send"
end
def bar
"bar"
end
private
def private_bar
"private_bar"
end
end
Foo.new.bar #=> "bar"
Foo.new.private_bar #=> NoMethodError(private method 'private_bar' called for #Foo)
Foo.new.send(:bar) #=> "send"
Foo.new.__send__(:bar) #=> "__send__"
Foo.new.public_send(:bar) #=> "bar"
Foo.new.send(:private_bar) #=> "send"
Foo.new.__send__(:private_bar) #=> "__send__"
Foo.new.public_send(:private_bar) #=> NoMethodError(private method 'private_bar' called for #Foo)
À la fin, essayez d'utiliser public_send pour éviter l'appel direct à la méthode privée au lieu d'utiliser __send__ ou send.