Типы классов Ruby и операторы case

135

В чем разница между

case item.class
when MyClass
  # do something here
when Array
  # do something different here
when String
  # do a third thing
end

и

case item.class
when MyClass.class
  # do something here
when Array.class
  # do something different here
when String.class
  # do a third thing
end

По какой-то причине первый из них иногда работает, а второй - нет, а иногда второй работает, а первый - нет. Зачем? Какой из них «правильный» способ сделать это?

Дейзи София Холлман
источник
1
Строка - это класс. Класс класса - это Class.
Volte
Обратите внимание, что MyClass === objиспользуется метод Module # ===, чтобы проверить, objявляется ли он экземпляром MyClass.
серджио

Ответы:

234

Вы должны использовать:

case item
when MyClass
...

У меня была такая же проблема: как отловить класс Errno :: ECONNRESET в "случае когда"?

Nakilon
источник
1
Спасибо! Извините за обман (или своего рода обман), но несколько поисковых запросов не нашли ответа на этот предыдущий вопрос. Кажется, что использование === оператором case - довольно распространенная проблема, теперь, когда я вижу, что это проблема. На это, вероятно, следует чаще указывать в учебных пособиях и тому подобном (но я уверен, что многие авторы учебников также не знают об этом).
Дейзи София Холлман,
4
Предупреждение, которое не упоминалось при использовании ActiveRecord. Метод ActiveRecord === при сравнении классов использует .is_a ?, что означает, что подклассы класса будут оценивать значение true в операторе case. github.com/rails/rails/blob/…
Джереми Бейкер,
61

Да, Накилон прав, вы должны знать, как оператор threequal === работает с объектом, указанным в whenпредложении. В рубине

case item
when MyClass
...
when Array
...
when String
...

действительно

if MyClass === item
...
elsif Array === item
...
elsif String === item
...

Поймите, что case вызывает метод threequal ( MyClass.===(item)например), и этот метод может быть определен так, чтобы делать все, что вы хотите, а затем вы можете использовать оператор case с точностью.

Фред
источник
Если да, arr = []то я заметил, что он if Array === arrбудет оцениваться как true, но if arr === Arrayбудет оцениваться как false. Может кто-нибудь помочь объяснить?
Daniel
4
=== - это просто метод, который можно определить для выполнения того, что хочет дизайнер класса. Помните также, что a === b на самом деле означает a. === b, поэтому, если вы поменяете местами a и b, вы можете получить другое поведение. Нет гарантии, что === коммутативен. Фактически, Array === Array имеет значение false, но Object === Object истинно, поэтому Array переопределяет семантику ===.
Fred
5

В Ruby имя класса - это константа, которая относится к объекту типа Class, описывающего конкретный класс. Это означает, что высказывание MyClassв Ruby эквивалентно высказываниюMyClass.class в Java.

obj.class это объект типа Class описывающего класс obj. Если obj.classесть MyClass, то objбыло создано с помощью MyClass.new(грубо говоря). MyClassэто объект типа, Classкоторый описывает любой объект, созданный с использованиемMyClass.new .

MyClass.class- это класс MyClassобъекта (это класс объекта типа, Classкоторый описывает любой объект, созданный с помощью MyClass.new). Другими словами, MyClass.class == Class.

Кен Блум
источник
1

Это зависит от характера вашей itemпеременной. Если это экземпляр объекта, например

t = 5

затем

t.class == Fixnum

но если это класс сам по себе, например

t = Array

тогда это будет Classобъект, поэтому

t.class == Class

РЕДАКТИРОВАТЬ : см. Как поймать класс Errno :: ECONNRESET в «случае, когда»? как заявил Накилон, поскольку мой ответ может быть неправильным.

разъем
источник
В Ruby все является «экземпляром объекта».
Эрик