Ruby Send vs __send__


151

Kavramını anlıyorum some_instance.sendama bunu neden her iki şekilde de adlandırabileceğinizi anlamaya çalışıyorum. Ruby Koans, aynı şeyi yapmak için birçok farklı yol sunmanın ötesinde bir neden olduğunu ima eder. İşte iki kullanım örneği:

class Foo
  def bar?
    true
  end
end

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

Bunun hakkında bir fikri olan var mı?

Yanıtlar:


242

Bazı sınıflar (örneğin, standart kütüphanenin soket sınıfı) send, ilgisi olmayan kendi yöntemlerini tanımlar Object#send. Bu nedenle, herhangi bir sınıftaki nesnelerle çalışmak istiyorsanız __send__, güvenli tarafta olmak için kullanmanız gerekir .

Şimdi bu sendsadece neden değil , neden var __send__. Sadece __send__isim olsaydı , sendkarışıklık olmadan diğer sınıflar tarafından kullanılabilirdi. Bunun nedeni sendilk önce var olan ve daha sonra adın senddiğer bağlamlarda da kullanılabileceği anlaşıldı , bu yüzden __send__eklendi (bu aynı şeyle idve object_idbu arada oldu).


8
Ayrıca, BasicObject (Ruby 1.9'da tanıtıldı) sadece sahip __send__değil send.
Andrew Marshall

İyi cevap. Eğer bahsedilirse daha iyi olabilir public_send, ki bu sendzaten her durumda tercih edilir .
Marc-André Lafortune

31

Eğer varsa gerçekten ihtiyaç sendnormalde yapacağını gibi davranmaya, kullanmak gerekir __send__(o olmamalı) overriden olmayacaktır çünkü. Kullanımı __send__, özellikle manipüle edilen sınıfın hangi yöntemleri tanımladığını bilmediğinizde metaprogramlamada kullanışlıdır. Geçersiz kılabilirdi send.

İzlemek:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

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

Geçersiz kılarsanız __send__Ruby bir uyarı verir:

uyarı: __send__ 'i yeniden tanımlamak ciddi sorunlara neden olabilir

Geçersiz kılmanın faydalı olacağı bazı durumlar send, mesaj geçişi, soket sınıfları vb. Gibi bu adın uygun olduğu yerler olabilir.


9

__send__ bu yüzden kazara fazla yazılamaz.

Neden gelince sendmevcut: Ben başkası için konuşamıyor ama object.send(:method_name, *parameters)bakışlar daha güzel object.__send__(:method_name, *parameters)kullandığım bu yüzden, sendben sürece ihtiyaç kullanmak __send__.


6

Bunun dışında başkalarının zaten söyledim ve hangi söyleyerek aşağı kaynar sendve __send__aynı yöntemle iki takma adlar, üçüncü ilgilenen olabilir edilir somwhat farklı olasılık, hangi public_send. Misal:

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

Güncelleme: Ruby 2.1 Module#includeve Module#extendyöntemler herkese açık hale geldiğinden, yukarıdaki örnek artık çalışmaz.


0

Send __send__, ve public_send arasındaki temel fark aşağıdaki gibidir.

  1. Gönder ve __send__teknik olarak Object'in yöntemini çağırmak için kullanılanla aynıdır, ancak temel fark, gönderme yöntemini herhangi bir uyarı yapmadan geçersiz kılabilmeniz ve geçersiz kılmanız durumunda __send__bir uyarı mesajı olmasıdır.

uyarı: yeniden tanımlama __send__ciddi sorunlara neden olabilir

Bunun nedeni, özellikle mücevherler veya kütüphanelerde kullanılacağı bağlam bilinmediğinde, çakışmalardan kaçınmak için her zaman __send__gönderme yerine kullanın .

  1. Send (veya __send__) ve public_send arasındaki fark , __send__bir nesnenin özel yöntemlerini send / çağırabilir ve public_send'in çağıramaz olmasıdır.
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)

Sonunda __send__ veya send yerine özel yönteme doğrudan çağrı yapmaktan kaçınmak için public_send'i kullanmayı deneyin.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.