微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在不将结果传递给 F# 中的调用者的情况下捕获特定类型的异常?

如何解决如何在不将结果传递给 F# 中的调用者的情况下捕获特定类型的异常?

我正在尝试捕获特定异常以获取用于日志记录的基础消息,但我不希望将异常传递回调用方法。我知道如何在 C#、F# 中执行此操作?嗯。

这是不针对特定异常类型工作的代码。我只是记录方法中发生了问题

        match remoteapiResponse.StatusCode with
        | HttpStatusCode.OK ->
            let! resp = remoteapiResponse.Content.ReadAsstringAsync() |> Async.AwaitTask
            try
                let IsSuccess = JObject.Parse(resp).["IsSuccess"].ToString().Trim().ToLower()
                match IsSuccess with
                | "true" -> ()
                | "false" -> remoteapiResponse.StatusCode <- HttpStatusCode.BadRequest
                | _ -> ()
            with 
            | _ ->  logger.LogError("Error Occurred during IsSuccess flag validation.") 
        | _ -> ()

在下面的代码中,我的目标是评估 NullReferenceException 类型,但它实际上可能是任何类型的错误

    match remoteapiResponse.StatusCode with
        | HttpStatusCode.OK ->
            let! resp = remoteapiResponse.Content.ReadAsstringAsync() |> Async.AwaitTask
            try
                let IsSuccess = JObject.Parse(resp).["IsSuccess"].ToString().Trim().ToLower()
                match IsSuccess with
                | "true" -> ()
                | "false" -> remoteapiResponse.StatusCode <- HttpStatusCode.BadRequest
                | _ -> ()
            with 
            | :? NullReferenceException as ne -> logger.LogError("Error Occurred during IsSuccess flag validation." + ne.Message) 
        | _ -> ()

我看到的问题是,当我指定一个异常类型时,异常会通过链向上传递到调用方法。第一种方法按原样工作,但它没有收集非常有用的信息。

第二种方法是捕获错误的更多细节的方法,但它总是将异常带入链中。

如何最小程度地修改我的第二版代码(如果可能)以防止错误传递回调用者? 很确定有一个简单的方法可以解决这个问题,但我不是 100% 在 F# 中如何

解决方法

就像在 C# 中一样,您可以在 with 块中使用多个模式。它们将按顺序匹配。使用第一个捕获您想要记录的异常,然后使用捕获所有模式来吞下所有其他异常:

try
  ...
with
| ?: NullReferenceException as ne -> logger.LogError ...
| _ -> ()

编辑以回应评论。

If ?: 是什么是 _ -> () 以及为什么需要它?

:?_ -> 都是“收获”。

在 F# 中,就像在 C# 中一样,一个可以有多个 catch 块 - 几个用于不同类型的异常,并且可选地,一个 catch-all 块用于其他块中未明确提及的所有异常类型。

例如,在 C# 中:

try { throw ... }
catch (NullReferenceException e) { Console.WriteLine("Null"); }
catch (NotImplementedException e) { Console.WriteLine("Not impl"); }
catch (InvalidOperationException e) { Console.WriteLine("Invalid op"); }
catch { Console.WriteLine("No idea what happened"); }

F# 中的等效代码:

try
  ...
with
| ?: NullReferenceException as e -> printfn "Null"
| ?: NotImplementedException as e -> printfn "Not impl"
| ?: InvalidOperationException as e -> printfn "Invalid op"
| _ -> printfn "No idea what happened"

F# 版本确实有更多的灵活性 - 例如您可以在 F#-defined exception types 上使用 when 守卫或深度模式匹配。但想法是一样的:多个接球手,连续测试,直到匹配一次。

另一件需要注意的事情是语法 :? 并不是异常处理所特有的。它是在类层次结构上进行模式匹配的语法。例如,您可以在常规函数中使用它:

let isString (o: obj) = 
  match o with 
  | :? string -> true
  | _ -> false

我认为这有点像是在陈述“最终”结束行动

“相当”和“有点”这两个词属于自然语言。另一方面,编程语言结构具有非常具体、严格定义的含义,在任何给定时刻可能符合也可能不符合您的直觉。

特别是,C#/F# 中的 finally 块与“捕获所有其他异常”不同。虽然异常捕获器仅在异常发生时执行,但 finally 块会执行不管是否发生异常

当没有异常发生时,finally 块照常在 try 之后执行。当异常发生时,finally 块在引发异常之前 执行,并且至关重要的是,在 finally 块执行完毕后,异常飞行会继续进行。再一次开车回家:finally不会阻止异常

finally 块的想法是允许您清理资源并确保无论是否发生异常都会进行清理。

在 C# 中:

try { Console.WriteLine("Working"); }
finally { Console.WriteLine("Done"); }

在 F# 中:

try
  printfn "Working"
finally
  printfn "Done"

另一件需要注意的事情是,与 C# 不同,由于复杂的原因,F# 不允许在同一块中同时包含 withfinally。因此,如果两者都需要,则必须将它们相互嵌套。

在 C# 中:

try { Console.WriteLine("Working"); }
catch (NullReferenceException e) { Console.WriteLine("Null"); }
finally { Console.WriteLine("Cleanup"); }

在 F# 中:

try
  try
    printfn "Working"
  with :? NullReferenceException ->
    printfn "Null"
finally
  printfn "Done"

有关详细信息,请参阅 the docs

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。