2017-04-05 49 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