Typy klas Ruby i instrukcje case

138

Jaka jest różnica pomiędzy

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

i

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

Z jakiegoś powodu pierwsza z nich czasami działa, a druga nie, a innym razem druga działa, a pierwsza nie. Czemu? Który z nich jest „właściwym” sposobem, aby to zrobić?

Daisy Sophia Hollman
źródło
1
Ciąg jest klasą. Klasa klasy to Class.
Volte
Zauważ, że MyClass === objużywa metody Module # === do sprawdzenia, czy objjest to wystąpienie MyClass.
sergio 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

237

Musisz użyć:

case item
when MyClass
...

Miałem ten sam problem: Jak wyłapać klasę Errno :: ECONNRESET w "przypadku kiedy"?

Nakilon
źródło
1
Dzięki! Przepraszam, że oszukuję (lub trochę oszukuję), ale kilka wyszukiwań nie przyniosło tego poprzedniego pytania. Wygląda na to, że użycie === w opisie przypadku jest dość powszechnym problemem, teraz, gdy widzę, na tym polega problem. Prawdopodobnie powinno być to częściej podkreślane w tutorialach i tym podobnych (ale założę się, że wielu autorów tutoriali również nie jest tego świadomych).
Daisy Sophia Hollman
4
Ostrzeżenie, o którym nie wspomniano, jeśli używasz ActiveRecord. Metoda ActiveRecord === w porównaniach klas używa .is_a?, Co oznacza, że ​​podklasy klasy będą zwracane jako prawda w instrukcji case. github.com/rails/rails/blob/…
Jeremy Baker
63

Tak, Nakilon ma rację, musisz wiedzieć, jak działa operator trzech równych === na obiekcie podanym w whenklauzuli. W Ruby

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

jest naprawdę

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

Zrozum, że przypadek wywołuje metodę trzech równych ( MyClass.===(item)na przykład) i tę metodę można zdefiniować tak, aby robiła, co chcesz, a następnie możesz użyć instrukcji case z precyzją

Fred
źródło
1
Jeśli arr = []zauważyłem, że if Array === arrto oceni jako prawda, ale if arr === Arrayoceni jako fałsz. Czy ktoś może pomóc wyjaśnić?
Daniel
5
=== to po prostu metoda, którą można zdefiniować tak, aby robiła to, czego oczekuje projektant klasy. Pamiętaj też, że a === b naprawdę oznacza a. === b, więc jeśli zamienisz a i b, możesz uzyskać inne zachowanie. Nie ma gwarancji, że === jest przemienne. W rzeczywistości Array === Array jest fałszywe, ale Object === Object jest true, więc Array redefiniuje semantykę ===.
Fred
14

Możesz użyć:

case item.class.to_s
    when 'MyClass'

... gdy następujący zwrot akcji nie jest możliwy:

case item
    when MyClass

Powodem tego jest to, że caseużywa ===, a relacja ===, którą opisuje operator, nie jest przemienna . Na przykład 5to Integer, ale jest ? Tak powinieneś myśleć o / .Integer5casewhen

user664833
źródło
5

W Rubim nazwa klasy jest stałą, która odnosi się do obiektu typu Classopisującego określoną klasę. Oznacza to, że mówienie MyClassw Rubim jest równoznaczne z mówieniem MyClass.classw Javie.

obj.classjest obiektem typu Classopisującym klasę obj. Jeśli obj.classtak MyClass, to objzostał utworzony przy użyciu MyClass.new(z grubsza mówiąc). MyClassto obiekt typu, Classktóry opisuje dowolny obiekt utworzony za pomocą MyClass.new.

MyClass.classto klasa MyClassobiektu (jest to klasa obiektu typu Classopisująca dowolny obiekt utworzony za pomocą MyClass.new). Innymi słowy MyClass.class == Class.

Ken Bloom
źródło
1

To zależy od charakteru twojej itemzmiennej. Jeśli jest to instancja obiektu, np

t = 5

następnie

t.class == Fixnum

ale jeśli jest to klasa sama w sobie np

t = Array

wtedy będzie to Classobiekt, więc

t.class == Class

EDYCJA : zapoznaj się z Jak złapać klasę Errno :: ECONNRESET w "przypadku kiedy"? jak stwierdził Nakilon, ponieważ moja odpowiedź może być błędna.

Jacek
źródło
W Rubim wszystko jest „instancją obiektu”.
Eric Duminil