2011-10-25 27 views
14

Cách được bảo đảm để so sánh hai đối tượng theo danh tính của chúng trong Ruby là gì? Với hai biến, tôi muốn trả về true nếu các biến trỏ đến cùng một đối tượng chính xác trong bộ nhớ.Cách được bảo đảm để so sánh các đối tượng theo danh tính trong Ruby

Đối với hầu hết đối tượng Ruby, phương pháp so sánh equal? bởi sắc:

f = g = Object.new 
p f.equal? g # => true 

Tuy nhiên, điều này không làm việc cho tất cả các đối tượng. Ví dụ:

class F 
    def ==(obj) false end 
    def ===(obj) false end 
    def eql?(obj) false end 
    def equal?(obj) false end 
    def object_id; self end 
end 

f = g = F.new 
p f == g  # => false 
p f === g  # => false 
p f.eql? g  # => false 
p f.equal? g # => false 
p f.object_id == g.object_id # => false 

Cách dễ dàng/bảo đảm để so sánh hai đối tượng theo danh tính không thể bị đánh bại là gì?

Đây là câu hỏi hoàn toàn về trí tuệ. Câu trả lời cho bất kỳ câu hỏi nào bắt đầu bằng "lý do" có thể sẽ là "Bởi vì tôi tò mò".

+0

Từ cho những thứ trong khoa học máy tính và toán học như F là gì? Có một số từ cho nó. Tôi muốn nói "ma quỷ", nhưng ít ác. –

+0

Tôi nghĩ từ này là "sai". Như trong, nó không phải là một điều bạn nên làm. – Chuck

+1

Tôi muốn sử dụng một từ bắt đầu bằng chữ "F" ...Không có đảm bảo trừ khi bạn muốn viết một phần mở rộng C, bạn phải lịch sự nhưng không có gì ngăn cản bất cứ ai từ một xã hội chống xã hội. –

Trả lời

12

Bạn có thể lấy phiên bản không bị ràng buộc của Object#object_id, liên kết nó với đối tượng được đề cập và xem nội dung được đề cập. Với lớp F của bạn với một bổ sung:

class F 
    # ... 
    def inspect; 'pancakes!' end # Just so we can tell what we have later. 
end 

Sau đó:

>> f = F.new 
>> f.object_id 
=> pancakes! 
>> unbound_object_id = Object.instance_method(:object_id) 
>> unbound_object_id.bind(f).call 
=> 2153000340 
>> ObjectSpace._id2ref(2153000340).inspect 
=> "pancakes!" 

Tất nhiên, nếu ai đó mở ra đối tượng và thay thế object_id sau đó bạn đang trên may mắn nhưng điều này sẽ là nhỏ nhất của bạn vấn đề nếu ai đó làm điều đó. Nếu bạn có thể lấy unbound_object_idUnboundMethod của mình trước khi bất kỳ thứ gì khác được tải, thì sẽ không thành vấn đề nếu ai đó thay đổi Object#object_id vì số unbound_object_id của bạn vẫn sẽ là bản chính xác.

Vì vậy, vòng này về hack cung cấp cho bạn một đáng tin cậy object_id cho bất kỳ đối tượng (tùy thuộc vào các caveats ở trên). Bây giờ bạn có thể lấy và so sánh các id đối tượng để có được sự so sánh đáng tin cậy của bạn.

+0

Tuyệt vời, nó hoạt động! Tôi chỉ muốn đóng góp một hàm sử dụng kỹ thuật này để so sánh hai biến theo định danh: 'def compare_by_identity (x, y); id = Object.instance_method: object_id; id.bind (x) .call == id.bind (y) .call; end' –

3

Câu trả lời bởi mu quá ngắn là rất tốt, nhưng có một cách khác bạn có thể làm điều đó bằng cách sử dụng tính năng đặc biệt của lớp Hash:

def compare_by_identity(x, y) 
    h = {}.compare_by_identity 
    h[x] = 1 
    h[y] = 2 
    h.keys.size == 1 
end 

Tính năng compare_by_identity đã được bổ sung trong Ruby 1.9. 2, do đó, chức năng này sẽ không hoạt động trong các phiên bản trước đó. Tôi nghĩ rằng mu là quá ngắn câu trả lời của là tốt hơn.

4

Sử dụng BasicObject#equal?. Theo số Ruby documentation:

Khác với ==, bằng nhau? phương thức không bao giờ được ghi đè bởi các lớp con: nó được sử dụng để xác định nhận diện đối tượng (tức là, a.equal? ​​(b) iff a là cùng một đối tượng với b).

Cần đảm bảo cao hơn? AFAIK không thể nhận được nó, BaseObject#object_id, ObjectSpace._id2ref, Hash#compare_by_identity có thể được ghi đè hoặc khỉ vá quá (ít nhất đây là niềm tin cá nhân của tôi).

+0

Bạn có thể cung cấp 'def bằng nhau? (khác) false end' thực hiện nếu bạn có một thái độ đủ hận thù. –

Các vấn đề liên quan