如何在“case when”语句中捕获Errno::ECONNRESET类?

33

我的应用程序(Ruby 1.9.2)可能会引发不同的异常,包括网络连接断开。我使用rescue Exception => e捕获异常,然后使用case/when以不同的方式处理它们,但有几个错误直接跳过了我的条件,进入了else

rescue Exception => e
    p e.class
    case e.class
        when Errno::ECONNRESET
            p 1
        when Errno::ECONNRESET,Errno::ECONNABORTED,Errno::ETIMEDOUT
            p 2
        else
            p 3
    end
end

输出:

Errno::ECONNRESET
3

1
在多重救援块中拯救你的错误类,这样你就可以完全避免使用 case 语句。 - Andy Triggs
2个回答

59

这是因为===运算符在Class类上的工作方式。

case语句在内部调用要评估的对象的===方法。如果您想测试e类,只需针对e进行测试,而不是e.class。这是因为e.class会进入when Class情况中,因为e.class是一个Class。

rescue Exception => e
    case e
        when Errno::ECONNRESET
            p 1
        when Errno::ECONNRESET,Errno::ECONNABORTED,Errno::ETIMEDOUT
            p 2
        else
            p 3
    end
end

是的,有时候 Ruby 的语法可能会有些奇怪。


5
是的,很奇怪。1 === 1 => true. Array === Array => false. - Nakilon
嗯,我敢打赌 Array === Class 会返回 true,但现在我很困惑。多亏了你的评论,现在我睡不着 :/ - Chubas
4
当然,现在有意义了。=== 是针对你要比较的元素调用的。因此,Class === ArrayString === "foobar"/foo/ === "foobar" 都会返回 true。 - Chubas

3

这取决于您所引用的是类还是常量。 例如,我不得不使用以下 case 语句来使某种类型的检测工作正常。

def fail(exception_error)
exception = exception_error
case exception.class
  when /HTTPClient::ConnectTimeoutError.new/
    status = 'CONNECTION TIMEOUT'
    connection_status = 'DOWN'
  else
    status = 'UNKNOWN FAILURE'
    connection_status = 'DOWN'
end

但这是因为我正在使用实际的异常类而不是常量。HTTPClient正在引发一个实际的类对象:

class TimeoutError < RuntimeError
end  
class ConnectTimeoutError < TimeoutError
end

以下是令人困惑的事实:

error = HTTPClient::ConnectTimeoutError.new
HTTPClient::ConnectTimeoutError === error
#=> true
error === HTTPClient::ConnectTimeoutError
#=> false

不确定该怎么理解。


“谜题事实”所运行的 Ruby 版本是什么? - David West

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接