2017-04-05 39 views
2

Ruby不是一種類型語言,因此我覺得這很奇怪。例如:Ruby不同類型的對象的成員資格

class Test 
    attr_reader :field 

    def initialize(f) 
     @field = String(f) 
    end 

    def ==(other) 
     field == String(other) 
    end 

    def eql?(other) 
     field.eql? String(other) 
    end 

    def hash 
     field.hash 
    end 

    def to_s 
     field 
    end 
end 

s = Set.new([Test.new('string')]) 
# => [#<Test:0x007fc97da17ea0 @field="string">] 

puts Test.new('string').eql?(Test.new('string')) 
# true 

puts Test.new('string').hash == Test.new('string').hash 
# true 

puts s.member? Test.new('string') 
# true 

puts s.member? Test.new('other') 
# false 

puts Test.new('string') == 'string' 
# true 

puts Test.new('string').eql? 'string' 
# true 

puts Test.new('string').hash == 'string'.hash 
# true 

但是,

puts s.member? 'string' 
# false 

好像紅寶石執行某種類型的內部檢查。這應該是這樣嗎?

回答

3

你的主要問題是String#eql?

puts Test.new('string').eql? 'string' 
# true 
puts 'string'.eql? Test.new('string') 
# false 

你說得對,似乎有一些內部的類型檢查:

rb_str_eql(VALUE str1, VALUE str2) 
{ 
    if (str1 == str2) return Qtrue; 
    if (!RB_TYPE_P(str2, T_STRING)) return Qfalse; 
    return str_eql(str1, str2); 
} 

注意,使用的其他方式,當你示例工作:

s = Set.new(['string']) 
puts s.member? Test.new('string') 
# true 

如果你真的想要達到這個目的,你可猴子補丁String#eql?

module TestStringEquality 
    def eql?(other) 
    other.is_a?(Test) ? other.eql?(self) : super 
    end 
end 

s = Set.new([Test.new('string')]) 
puts s.member? 'string' 
# false 

class String 
    prepend TestStringEquality 
end 

puts s.member? 'string' 
# true 

要小心,這種方法在每一個創業板很可能使用。

最後,請記住Set是一個純粹的Ruby實現,Hash作爲後端。這可能會讓Google更容易獲取信息和文檔。

+1

您可以添加一個類型檢查('other.is_a?Test')來獲得侵入性較小的補丁。 – Stefan

+0

似乎工作:http://ideone.com/7fibfL – Stefan

+1

我可能會將該方法移動到一個模塊,並使用'refine'或'prepend'有一個適當的'super'方法。然後,您可以將比較委託給'other'(按相反順序),或者只是調用'super',即:'other.is_a?(Test)? other.eql?(self):super' – Stefan