Wysyłanie w Ruby kontra __wysyłanie__


151

Rozumiem koncepcję, some_instance.sendale próbuję dowiedzieć się, dlaczego można to nazwać w obie strony. Ruby Koans sugeruje, że istnieje jakiś powód, dla którego nie można zapewnić wielu różnych sposobów zrobienia tego samego. Oto dwa przykłady użycia:

class Foo
  def bar?
    true
  end
end

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

Czy ktoś ma o tym pojęcie?

Odpowiedzi:


242

Niektóre klasy (na przykład klasa gniazda biblioteki standardowej) definiują własne sendmetody, z którymi nie ma nic wspólnego Object#send. Więc jeśli chcesz pracować z obiektami dowolnej klasy, musisz __send__być po bezpiecznej stronie.

Pozostaje pytanie, dlaczego istnieje, senda nie tylko __send__. Gdyby istniała tylko __send__nazwa, sendmogłaby być używana przez inne klasy bez żadnego zamieszania. Powodem tego jest to, że sendistniało jako pierwsze, a dopiero później zdano sobie sprawę, że nazwa sendmoże być użyteczna również w innych kontekstach, więc __send__została dodana (to samo stało się z idi object_idprzy okazji).


8
Również BasicObject (wprowadzony w Ruby 1.9) ma tylko __send__, nie send.
Andrew Marshall,

Dobra odpowiedź. Mogłoby być jeszcze lepiej, gdyby zostało wspomniane public_send, co często jest lepsze niż i sendtak.
Marc-André Lafortune

31

Jeśli naprawdę trzeba sendzachowywać się jak to zwykle zrobić, należy użyć __send__, ponieważ nie będzie (nie powinien) być nadpisane. Używanie __send__jest szczególnie przydatne w metaprogramowaniu, gdy nie wiesz, jakie metody definiuje manipulowana klasa. Mogło się zepsuć send.

Zegarek:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

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

Jeśli zastąpisz __send__, Ruby wyemituje ostrzeżenie:

ostrzeżenie: przedefiniowanie `__send__ 'może spowodować poważne problemy

Niektóre przypadki, w których warto przesłonić, sendto takie, w których ta nazwa jest odpowiednia, na przykład przekazywanie wiadomości, klasy gniazd itp.


9

__send__ istnieje, więc nie można go przypadkowo nadpisać.

Jak, dlaczego sendistnieje: Nie mogę mówić za kogoś innego, ale object.send(:method_name, *parameters)wygląda ładniej niż object.__send__(:method_name, *parameters), więc używam send, chyba że trzeba użyć __send__.


6

Oprócz tego, co powiedzieli ci inni i co sprowadza się do powiedzenia tego sendi __send__są to dwa aliasy tej samej metody, możesz być zainteresowany trzecią, nieco inną możliwością, czyli public_send. Przykład:

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

Aktualizacja: Od Ruby 2.1 Module#includei Module#extendmetody stały się publiczne, więc powyższy przykład już nie zadziała.


0

Główna różnica między send __send__, i public_send jest następująca.

  1. send i __send__są technicznie takie same, jak używane do wywołania metody Object, ale główna różnica polega na tym, że możesz przesłonić metodę wysyłania bez żadnego ostrzeżenia, a kiedy nadpisujesz, __send__pojawia się komunikat ostrzegawczy

ostrzeżenie: przedefiniowanie __send__może spowodować poważne problemy

Dzieje się tak, ponieważ aby uniknąć konfliktów, szczególnie w klejnotach lub bibliotekach, gdy kontekst, w którym zostanie użyty, jest nieznany, zawsze używaj __send__zamiast wysyłania.

  1. Różnica między send (or __send__) i public_send polega na tym, że send / __send__can wywołać metody prywatne obiektu, a public_send nie może.
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)

Na koniec spróbuj użyć public_send, aby uniknąć bezpośredniego wywołania metody prywatnej zamiast używania __send__ lub send.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.