루비 보내기 대 __send__


151

나는 개념을 이해 some_instance.send하지만 왜 이것을 두 가지 방법으로 부를 수 있는지 알아 내려고 노력 중입니다. Ruby Koans는 동일한 작업을 수행하는 다양한 방법을 제공하는 것 이상의 이유가 있음을 암시합니다. 사용 예는 다음과 같습니다.

class Foo
  def bar?
    true
  end
end

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

누구든지 이것에 대해 알고 있습니까?

답변:


242

일부 클래스 (예 : 표준 라이브러리의 소켓 클래스) send는 관련이없는 자체 메서드를 정의합니다 Object#send. 따라서 모든 클래스의 객체로 작업하려면 다음을 사용해야합니다.__send__ 하려면 안전한면에 있어야합니다.

이제 그 질문이 남았습니다. 왜 그런지 send아닌지 __send__. 단지이 있다면 __send__이름이 send혼동없이 다른 클래스에 의해 사용될 수 있습니다. 그 이유는 즉 send최초의 존재 만 나중에는 이름이 실현되었다 send그래서,도 유용하게 다른 상황에서 사용될 수있다 __send__(무슨 일이 있었 같은 일이이 추가되었다 idobject_id방법에 의해).


8
또한 BasicObject (루비 1.9 도입이) 만 보유 __send__하지 send.
Andrew Marshall

좋은 대답입니다. 더 나은이 언급 한 경우 될 수 public_send종종 바람직하다, send어쨌든.
Marc-André Lafortune

31

당신이 경우 정말 필요 send정상적으로 할 것처럼 행동하려면 사용해야 __send__은 (는 안된다) 오버라이드 (override) 할 수 없기 때문에. 사용 __send__은 조작하는 클래스가 어떤 메소드를 정의하는지 모르는 경우 메타 프로그래밍에 특히 유용합니다. 재정의했을 수 있습니다 send.

손목 시계:

class Foo
  def bar?
    true
  end

  def send(*args)
    false
  end
end

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

을 무시하면 __send__, Ruby는 경고를냅니다 :

경고 :`__send__ '를 재정의하면 심각한 문제가 발생할 수 있습니다

재정의하는 send것이 유용한 경우 는 메시지 전달, 소켓 클래스 등과 같이 해당 이름이 적절한 경우 가 있습니다.


9

__send__ 실수로 덮어 쓸 수 없도록 존재합니다.

send존재 하는지에 관해서는 : 나는 다른 사람을 위해 말할 수는 없지만 object.send(:method_name, *parameters)보다 더 멋지게 보입니다 object.__send__(:method_name, *parameters). 따라서 사용할 필요send없으면 사용 합니다.__send__


6

그렇다에서 무엇을 다른 사람이 이미 당신에게 무엇을 말하는 아래로 비등 send__send__같은 방법으로 두 개의 별칭, 세 번째에 관심이있을 수 있습니다, somwhat 다른 가능성이다 public_send. 예:

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

업데이트 : 루비 2.1, 이후 Module#includeModule#extend공공 될 방법, 위의 예는 더 이상 작동하지 않을 것 때문에.


0

send __send__, 및 public_send 의 주요 차이점은 다음과 같습니다.

  1. send __send__는 기술적으로 Object의 메소드를 호출하는 데 사용되는 것과 동일하지만 가장 큰 차이점은 경고없이 send 메소드를 대체 할 수 있으며 무시할 때 __send__경고 메시지가 있다는 것입니다

경고 : 재정의 __send__하면 심각한 문제가 발생할 수 있습니다

이는 사용될 컨텍스트를 알 수 없을 때 특히 gem이나 라이브러리에서 충돌을 피하기 위해 항상 __send__send 대신 사용하십시오 .

  1. send (또는 __send__)와 public_send 의 차이점은 send __send__는 개체의 개인 메서드를 호출 / 호출 할 수 있고 public_send는 호출 할 수 없다는 것입니다.
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)

결국 __send__ 또는 send 대신 private 메소드에 대한 직접 호출을 피하려면 public_send를 사용하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.