Ruby-Klassentypen und case-Anweisungen

134

Was ist der Unterschied zwischen

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

und

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

Aus irgendeinem Grund funktioniert die erste manchmal und die zweite nicht, und manchmal funktioniert die zweite und die erste nicht. Warum? Welches ist der "richtige" Weg, es zu tun?

Daisy Sophia Hollman
quelle
1
String ist eine Klasse. Die Klasse einer Klasse ist Klasse.
Volte
Beachten Sie, dass MyClass === objdie Methode Module # === verwendet wird, um zu überprüfen, ob objes sich um eine Instanz von handelt MyClass.
Sergio

Antworten:

233

Sie müssen verwenden:

case item
when MyClass
...

Ich hatte das gleiche Problem: Wie fange ich die Errno :: ECONNRESET-Klasse in "case when" ab?

Nakilon
quelle
1
Vielen Dank! Es tut mir leid, dass ich betrogen habe (oder irgendwie betrogen), aber bei mehreren Suchanfragen wurde diese vorherige Frage nicht angezeigt. Es scheint, dass die Verwendung von === durch die case-Anweisung ein weit verbreitetes Problem ist, jetzt wo ich sehe, dass dies das Problem ist. Dies sollte wahrscheinlich häufiger in Tutorials und dergleichen erwähnt werden (aber ich wette, dass viele Tutorial-Autoren dies auch nicht wissen).
Daisy Sophia Hollman
4
Eine Einschränkung, die bei Verwendung von ActiveRecord nicht erwähnt wurde. Die ActiveRecord === -Methode für Klassenvergleiche verwendet .is_a?, Was bedeutet, dass Unterklassen einer Klasse in der case-Anweisung als true ausgewertet werden. github.com/rails/rails/blob/…
Jeremy Baker
60

Ja, Nakilon ist richtig. Sie müssen wissen, wie der Operator threequal === für das in der whenKlausel angegebene Objekt funktioniert . In Ruby

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

ist wirklich

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

Verstehen Sie, dass case eine dreifache Methode aufruft ( MyClass.===(item)zum Beispiel), und dass diese Methode definiert werden kann, um zu tun, was Sie wollen, und dann können Sie die case-Anweisung mit präzise W verwenden

Fred
quelle
Wenn ich arr = []dann habe ich bemerkt, dass if Array === arrdas als wahr, aber if arr === Arrayals falsch bewertet wird. Kann mir bitte jemand helfen zu erklären?
Daniel
4
=== ist nur eine Methode, die definiert werden kann, um das zu tun, was der Designer einer Klasse möchte. Denken Sie auch daran, dass a === b wirklich a bedeutet. === b. Wenn Sie also a und b wechseln, können Sie ein anderes Verhalten erzielen. Es gibt keine Garantie dafür, dass === kommutativ ist. Tatsächlich ist Array === Array falsch, aber Object === Object ist wahr, sodass Array die Semantik von === neu definiert.
Fred
13

Sie können verwenden:

case item.class.to_s
    when 'MyClass'

... wenn folgende Drehung nicht möglich ist:

case item
    when MyClass

Der Grund dafür ist, dass caseverwendet ===wird und die vom ===Operator beschriebene Beziehung nicht kommutativ ist . Ist zum Beispiel 5ein Integer, aber ist Integerein 5? So sollten Sie sich case/ vorstellen when.

user664833
quelle
5

In Ruby ist ein Klassenname eine Konstante, die sich auf ein Objekt vom Typ bezieht Class, das eine bestimmte Klasse beschreibt. Das bedeutet, dass das Sprichwort MyClassin Ruby dem Sprichwort MyClass.classin Java entspricht.

obj.classist ein Objekt vom Typ Class, das die Klasse von beschreibt obj. Wenn obj.classja MyClass, dann objwurde mit MyClass.new(grob gesagt) erstellt. MyClassist ein Objekt vom Typ Class, das jedes Objekt beschreibt, das mit erstellt wurde MyClass.new.

MyClass.classist die Klasse des MyClassObjekts (es ist die Klasse des Objekts vom Typ Class, die jedes Objekt beschreibt, das mit erstellt wurde MyClass.new). Mit anderen Worten MyClass.class == Class.

Ken Bloom
quelle
1

Dies hängt von der Art Ihrer itemVariablen ab. Wenn es sich um eine Instanz eines Objekts handelt, z

t = 5

dann

t.class == Fixnum

aber wenn es eine Klasse für sich ist, z

t = Array

dann wird es ein ClassObjekt sein, also

t.class == Class

BEARBEITEN : Bitte lesen Sie Wie man die Errno :: ECONNRESET-Klasse in "case when" abfängt. wie von Nakilon angegeben, da meine Antwort falsch sein könnte.

Jack
quelle
In Ruby ist alles "eine Instanz eines Objekts".
Eric Duminil