Почему оператор switch выполняет проверку на равенство иначе, чем оператор if? - PullRequest
3 голосов
/ 14 ноября 2011

Почему оператор if работает в следующем примере, в то время как оператор switch не работает.

  • работает:

    if ''.class == String
      puts "yep, that's a string"
    end
    
  • не работает:

    case ''.class
    when String
      puts "yep, that's a string, but this case is never triggered"
    end
    

В приведенном выше тривиальном примере оператор switch является избыточным, но, очевидно, существуют ситуации, когда оператор switch будет DRYer чемприкованный elsif с

Ответы [ 4 ]

11 голосов
/ 14 ноября 2011

На самом деле, "case" в ruby ​​сравнивается с ===

Так что ваш пример эквивалентен:

if ''.class === String
   puts "yep, that's a string"
end
5 голосов
/ 15 ноября 2011

Это потому, что оператор case не использует оператор ==, он использует оператор === (иногда называемый оператором равенства регистра).Что это делает, зависит от того, что находится слева от оператора.Итак, если бы вы должны были преобразовать оператор case следующим образом:

case "Some string"
when String
  puts "It's a string!"
else
  puts "It's not a string!"
end

В оператор if, он стал бы таким:

if String === "Some string"
  puts "It's a string!"
else
  puts "It's not a string!"
end

Обратите внимание, что Ruby делает это в обратном направлении от того, что вы ожидаете, это String === "Some string".Это потому, что вы действительно хотите позвонить Class#=== здесь, а не String#===.То, что оператор === делает для любого объекта, действительно зависит от класса.В случае Class#=== это примерно эквивалентно вызову "Some string".is_a?(String).Но если вы сделаете "a" === "b", метод String#=== будет примерно эквивалентен String#==.

Это может запутать, но использование оператора в значительной степени идиоматично.Другими словами, идиома «объект класса в операторе when» означает проверку того, принадлежит ли объект case этого класса.Я написал статью об этом, которая объясняет это немного больше, вы можете прочитать это здесь .

5 голосов
/ 14 ноября 2011

Быстрый и простой ответ заключается в том, что в этом случае используется === (3 равно), а не два.

$ irb                                                                 
if ''.class == String
  puts "yep, that's a string"   
end 

Да, это строка

=> nil

if ''.class === String
  puts "yep, that's a string"
end
=> nil
3 голосов
/ 15 ноября 2011

Как уже говорили другие, case равенство в Ruby работает немного иначе, чем вы могли бы ожидать, поэтому вы можете просто сделать

case foo
when String # that is, when String === foo, more or less when foo.class == String
  do something
end

Но обычно вы не должны ,Если вы явно тестируете имена классов, то (обычно) ваш дизайн ОО имеет недостатки - в большинстве случаев вам следует вместо этого использовать полиморфизм.Другими словами, вместо

if x.class == String
  x.process_string
else
  x.process_non_string
end

вы просто должны иметь x.process, а затем определять process по-другому для String и других классов.Очиститель, меньше кода, не заставляет вызывающую программу знать класс вызываемого объекта.

...