2011-01-11 19 views
137

Tôi hiểu khái niệm về some_instance.send nhưng tôi đang cố gắng tìm ra lý do tại sao bạn có thể gọi cả hai cách này. Ruby Koans ngụ ý rằng có một số lý do ngoài việc cung cấp nhiều cách khác nhau để làm điều tương tự. Dưới đây là hai ví dụ về cách sử dụng:Ruby gửi vs __send__

class Foo 
    def bar? 
    true 
    end 
end 

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

Bất kỳ ai có ý tưởng gì về điều này?

Trả lời

213

Một số lớp (ví dụ: lớp ổ cắm của thư viện chuẩn) xác định phương thức send của riêng chúng không liên quan gì đến Object#send. Vì vậy, nếu bạn muốn làm việc với các đối tượng của bất kỳ lớp nào, bạn cần sử dụng __send__ để ở bên an toàn.

Bây giờ, để lại câu hỏi, tại sao có send và không chỉ __send__. Nếu chỉ có __send__ tên send có thể được sử dụng bởi các lớp khác mà không có bất kỳ sự nhầm lẫn nào. Lý do cho điều đó là send tồn tại trước và chỉ sau đó người ta nhận ra rằng tên send cũng có thể được sử dụng trong các ngữ cảnh khác, vì vậy __send__ được thêm vào (đó là điều tương tự đã xảy ra với idobject_id bằng cách này).

+7

Ngoài ra, [BasicObject] (http://ruby-doc.org/core/BasicObject.html) (được giới thiệu trong Ruby 1.9) chỉ có '__send__', chứ không phải' send'. –

+0

Câu trả lời hay.Thậm chí có thể tốt hơn nếu nó đề cập đến 'public_send', thường thích hợp hơn với' send' anyways. –

29

Nếu bạn thực sự cần send để hoạt động như bình thường, bạn nên sử dụng __send__, vì nó sẽ không (không nên) bị ghi đè. Sử dụng __send__ đặc biệt hữu ích trong lập trình meta, khi bạn không biết những phương thức nào mà lớp đang được thao tác định nghĩa. Nó có thể đã làm quá mức send.

Watch:

class Foo 
    def bar? 
    true 
    end 

    def send(*args) 
    false 
    end 
end 

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

Nếu bạn ghi đè __send__, Ruby sẽ phát ra một cảnh báo:

cảnh báo: xác định lại '__send__' có thể gây ra vấn đề nghiêm trọng

Một số trường hợp sẽ rất hữu ích khi ghi đè lên send sẽ là nơi tên đó phù hợp, như tin nhắn pas các lớp học hát, ổ cắm, v.v.

9

__send__ tồn tại để nó không thể bị viết quá một cách tình cờ.

Đối với lý do tại sao send tồn tại: Tôi không thể nói cho bất cứ ai khác, nhưng object.send(:method_name, *parameters) trông đẹp hơn object.__send__(:method_name, *parameters), vì vậy tôi sử dụng send trừ khi tôi cần sử dụng __send__.

5

Ngoài những gì người khác đã nói với bạn, và những gì nói xuống rằng send__send__ là hai bí danh của cùng một phương pháp, bạn có thể quan tâm đến khả năng khác thứ ba, somwhat, là public_send. Ví dụ:

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 

Cập nhật: Kể từ của Ruby 2.1, Module#includeModule#extend phương pháp trở thành công cộng, vì vậy ví dụ trên sẽ không có tác dụng nữa.

+0

kiến ​​thức tốt, cảm ơn! – jaydel