使用`===`(包含运算符)比较类

时间:2013-12-10 15:30:06

标签: ruby class types switch-statement comparison-operators

TypeOfClass === TypeOfClass false field.class这个事实让我感到反直觉。在以下代码中,即使false是同一个类,它的计算结果为case field.class when Fixnum, Float field + other_field when Date, DateTime, Time field else puts 'WAT?' end

Fixnum === Fixnum # => false
Class === Class   # => true

我这样做了:

Integer === 3 # => true
Fixnum === 3  # => true
3.class       # => Fixnum

我找到another thread

Numeric === Integer

我找不到这种语言设计的原因。语言设计师在这种行为中的想法是什么?

我认为这与another thread中提供的答案相关。假设Integer Numeric是更具体的Numeric === Integer #=> false 类型,这并不是不自然的。但是,它不是:

case

我认为===语句或Numeric需要谨慎。如果此运算符属于we think it is ,则Numeric应为IntegerNumeric应为true等。

有没有人解释为什么此功能不会扩展到类?如果比较的类是类'祖先'的成员,似乎很容易返回Time

根据下面提交的答案,该代码最初是对Timeframe = Struct.new(:from, :to) do def end_date case self.to when Fixnum, Float self.from + self.to when Date, DateTime, Time self.to else raise('InvalidType') end end end 进行分类(由ActiveSupport:CoreExtensions::Integer::TimeActiveSupport:CoreExtensions::Float::Time进行了扩展):

tf = Timeframe.new(Time.now, 5.months)
# => #<struct Timeframe from=Tue Dec 10 11:34:34 -0500 2013, to=5 months>
tf.end_date
# => RuntimeError: InvalidType
#  from timeframe.rb:89:in `end_date'

在控制台中,我得到:

{{1}}

3 个答案:

答案 0 :(得分:5)

我真的没有看到这里的问题。对于类,大小写相等运算符会询问左手参数是否是类(或任何子类)的实例。所以Fixnum === Fixnum真的问:“Fixnum类本身是Fixnum的子类吗?”不,不是。

班级Class本身是一个班级吗? Class === Class,是的。

操作员的观点是你不需要去找班级。在开头没有.class方法的情况下使用case语句有什么问题?

case field
when Fixnum, Float
  field + other_field
when Date, DateTime, Time
  field
else
  puts 'WAT?'
end

如果你有一个更复杂的例子,你可以编写自己的lambdas来简化case语句:

field_is_number =  -> x {[Fixnum, Float].include? x.class}
field_is_time   =  -> x {[Date, DateTime, Time].include? x.class}

case field.class
  when field_is_number
    field + other_field
  when field_is_time
    field
  else
    puts 'WAT?'
end

答案 1 :(得分:1)

如果其中一个操作数是一个类,则检查第二个操作数是否为此类实例。如果它们都是类,则它将返回false,除非它们中至少有一个是Class。

请注意,Class既是一个类又是一个自身的实例 - 它可能是最大的ruby奇怪的TBH,但它非常有意义。

我投票支持保持这种逻辑的原因是我们可以编写那些不错的case语句而不向对象添加.class

要点:

ClassName === object    <=>    object.kind_of? ClassName

但是,如果你真的想要覆盖这个用途:

class Class
  def ===(object)
    return object == self if object.is_a? Class
    super
  end
end

答案 2 :(得分:1)

那是Module#===及其预期行为:

  

mod === obj→true或false

     

案例平等 - 如果 anObject mod 的实例或其中一个,则返回true    mod 的后代。模块的用途有限,但可以在case中使用   用于按类对对象进行分类的语句。

它只返回obj.kind_of? mod

Fixnum === Fixnum      #=> false
Fixnum.kind_of? Fixnum #=> false

Class === Class        #=> true
Class.kind_of? Class   #=> true

String === "foo"       #=> true
"foo".kind_of? String  #=> true
由于其类层次结构,

3IntegerFixnum

3.kind_of? Integer     #=> true
3.kind_of? Fixnum      #=> true
3.class.ancestors      #=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]

Numeric不是Integer,而是Class

Numeric.kind_of? Integer  #=> false
Numeric.kind_of? Class    #=> true

3(2/3)1.23都是Numeric

3.kind_of? Numeric               #=> true
Rational(2, 3).kind_of? Numeric  #=> true
1.23.kind_of? Numeric            #=> true

底线:对于case语句,只需使用case obj代替case obj.class

<强>更新

您收到此错误是因为5.months未返回Integer,而是ActiveSupport::Duration

Integer === 5.months                 #=> false
ActiveSupport::Duration === 5.months #=> true

使用5.months.to_i调用您的方法或将ActiveSupport::Duration添加到您的类中应修复它。