Tipos de classe Ruby e instruções de caso

135

Qual é a diferença entre

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

e

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

Por alguma razão, o primeiro deles funciona às vezes e o segundo não, e outras vezes, o segundo funciona e o primeiro não. Por quê? Qual é a maneira "correta" de fazer isso?

Daisy Sophia Hollman
fonte
1
String é uma classe. A classe de uma classe é Class.
Volte
Observe que MyClass === objusa o método Module # === para verificar se objé uma instância de MyClass.
sergio

Respostas:

234

Você deve usar:

case item
when MyClass
...

Eu tive o mesmo problema: Como pegar a classe Errno :: ECONNRESET em "case when"?

Nakilon
fonte
1
Obrigado! Desculpe enganar (ou meio que enganar), mas várias pesquisas não apareceram na pergunta anterior. Parece que o uso de === pela declaração de caso é um problema bastante comum, agora que vejo que esse é o problema. Provavelmente, isso deve ser apontado com mais frequência nos tutoriais e afins (mas aposto que muitos escritores de tutoriais também não sabem disso).
Daisy Sophia Hollman
4
Uma ressalva que não foi mencionada se você estiver usando o ActiveRecord. O método ActiveRecord === nas comparações de classes usa .is_a ?, o que significa que as subclasses de uma classe serão avaliadas como verdadeiras na instrução case. github.com/rails/rails/blob/…
Jeremy Baker,
61

Sim, Nakilon está correto, você deve saber como o operador threequal === funciona no objeto fornecido na whencláusula. Em Ruby

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

é realmente

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

Entenda que o caso está chamando um método threequal ( MyClass.===(item)por exemplo) e esse método pode ser definido para fazer o que você quiser e, em seguida, você pode usar a instrução case com precisionw

Fred
fonte
Se eu tenho arr = [], notei que if Array === arravaliará como verdadeiro, mas if arr === Arrayavaliará como falso. Alguém por favor pode ajudar a explicar?
Daniel
4
=== é apenas um método que pode ser definido para fazer o que o designer de uma classe deseja. Lembre-se, também, que a === b realmente significa a. === b, portanto, se você alternar aeb, poderá obter um comportamento diferente. Não há garantia de que === seja comutativo. De fato, Matriz === Matriz é falsa, mas Objeto === Objeto é verdadeiro, portanto Matriz está redefinindo a semântica de ===.
21313 Fred Fred
13

Você pode usar:

case item.class.to_s
    when 'MyClass'

... quando a seguinte torção não for possível:

case item
    when MyClass

A razão para isso é que os caseusos ===e o relacionamento que o ===operador descreve não são comutativos . Por exemplo, 5é um Integer, mas é Integerum 5? É assim que você deve pensar em case/ when.

user664833
fonte
5

No Ruby, um nome de classe é uma constante que se refere a um objeto do tipo Classque descreve uma classe específica. Isso significa que dizer MyClassem Ruby é equivalente a dizer MyClass.classem Java.

obj.classé um objeto do tipo que Classdescreve a classe de obj. Se obj.classfor MyClass, então objfoi criado usando MyClass.new(grosso modo). MyClassé um objeto do tipo Classque descreve qualquer objeto criado usando MyClass.new.

MyClass.classé a classe do MyClassobjeto (é a classe do objeto do tipo Classque descreve qualquer objeto criado usando MyClass.new). Em outras palavras MyClass.class == Class,.

Ken Bloom
fonte
1

Depende da natureza da sua itemvariável. Se for uma instância de um objeto, por exemplo

t = 5

então

t.class == Fixnum

mas se é uma classe em si mesma, por exemplo

t = Array

então será um Classobjeto, então

t.class == Class

EDIT : consulte a seção Como capturar a classe Errno :: ECONNRESET em "case when"? como afirmado por Nakilon, pois minha resposta pode estar errada.

Jack
fonte
No Ruby, tudo é "uma instância de um objeto".
Eric Duminil