Types de classe Ruby et instructions de cas

135

Quelle est la différence entre

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

et

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

Pour une raison quelconque, le premier de ces travaux fonctionne parfois et le second ne fonctionne pas, et d'autres fois, le second fonctionne et le premier ne fonctionne pas. Pourquoi? Quelle est la «bonne» façon de le faire?

Daisy Sophia Hollman
la source
1
String est une classe. La classe d'une classe est Classe.
Volte
Notez qu'il MyClass === objutilise la méthode Module # === pour vérifier s'il objs'agit d'une instance de MyClass.
sergio

Réponses:

234

Tu dois utiliser:

case item
when MyClass
...

J'ai eu le même problème: Comment attraper la classe Errno :: ECONNRESET dans "cas quand"?

Nakilon
la source
1
Merci! Désolé de duper (ou une sorte de dupe), mais plusieurs recherches n'ont pas révélé la question précédente. Il semble que l'utilisation de === par l'instruction case soit un problème assez courant, maintenant que je vois que c'est le problème. Cela devrait probablement être souligné plus souvent dans les tutoriels et autres (mais je parie que de nombreux rédacteurs de tutoriels ne le savent pas non plus).
Daisy Sophia Hollman
4
Une mise en garde qui n'a pas été mentionnée si vous utilisez ActiveRecord. La méthode ActiveRecord === sur les comparaisons de classes utilise .is_a ?, ce qui signifie que les sous-classes d'une classe seront évaluées à true dans l'instruction case. github.com/rails/rails/blob/…
Jeremy Baker
61

Ouais, Nakilon a raison, vous devez savoir comment l'opérateur threequal === fonctionne sur l'objet donné dans la whenclause. En rubis

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

est vraiment

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

Comprenez que case appelle une méthode threequal ( MyClass.===(item)par exemple), et que cette méthode peut être définie pour faire ce que vous voulez, puis vous pouvez utiliser l'instruction case avec precisionw

Fred
la source
Si je l'ai fait, arr = []j'ai remarqué que if Array === arrcela évaluera à vrai mais if arr === Arrayévaluera à faux. Quelqu'un peut-il s'il vous plaît aider à expliquer?
Daniel
4
=== est juste une méthode qui peut être définie pour faire tout ce que le concepteur d'une classe veut qu'il fasse. Souvenez-vous également que a === b signifie vraiment a. === b, donc si vous changez de a et de b, vous pouvez obtenir un comportement différent. Il n'y a aucune garantie que === soit commutatif. En fait, Array === Array est faux, mais Object === Object est vrai, donc Array redéfinit la sémantique de ===.
Fred
13

Vous pouvez utiliser:

case item.class.to_s
    when 'MyClass'

... lorsque la torsion suivante n'est pas possible:

case item
    when MyClass

La raison en est que caseutilise ===, et la relation ===décrite par l' opérateur n'est pas commutative . Par exemple, 5est un Integer, mais est Integerun 5? C'est ainsi que vous devriez penser à case/ when.

user664833
la source
5

Dans Ruby, un nom de classe est une constante qui fait référence à un objet de type Classqui décrit une classe particulière. Cela signifie que dire MyClassen Ruby équivaut à dire MyClass.classen Java.

obj.classest un objet de type Classdécrivant la classe de obj. Si obj.classest MyClass, alors a objété créé en utilisant MyClass.new(grosso modo). MyClassest un objet de type Classqui décrit tout objet créé à l'aide de MyClass.new.

MyClass.classest la classe de l' MyClassobjet (c'est la classe de l'objet de type Classqui décrit tout objet créé à l'aide de MyClass.new). En d' autres termes, MyClass.class == Class.

Ken Bloom
la source
1

Cela dépend de la nature de votre itemvariable. S'il s'agit d'une instance d'un objet, par exemple

t = 5

puis

t.class == Fixnum

mais si c'est une classe en soi par exemple

t = Array

alors ce sera un Classobjet, donc

t.class == Class

EDIT : veuillez vous référer à Comment attraper la classe Errno :: ECONNRESET dans "cas quand"? comme indiqué par Nakilon puisque ma réponse pourrait être fausse.

Jack
la source
Dans Ruby, tout est "une instance d'un objet".
Eric Duminil