在这种情况下,为什么模式匹配在异常上失败?

时间:2011-11-24 22:36:59

标签: f#

我有这个简单的异常层次结构:

type FirstLevelException(msg) = inherit System.Exception (msg)
type SecondLevelException(msg, inner) = inherit System.Exception (msg, inner)
type ThirdLevelException(msg, inner) = inherit System.Exception (msg, inner)

和这三个(虚拟)功能:

member this.FirstFunction a =
    raise (new FirstLevelException("one"))

member this.SecondFunction a =
    try
        this.FirstFunction a
    with
    | :? FirstLevelException as ex -> raise (new SecondLevelException("two", ex))

member this.ThirdFunction a =
    try
        this.SecondFunction 25
    with
    | :? SecondLevelException as ex -> raise (new ThirdLevelException("three", ex))

当您调用ThirdFunction时很容易看到:

  • firstFunction引发FirstLevelException
  • secondFunction捕获它,将其包装到SecondLevelException并抛出它
  • thirdFunction捕获它,将其包装到ThirdLevelException并抛出它
  • 调用者可以捕获ThirdLevelException。

一切都好。现在我按以下方式更改thirdFunction:

member this.ThirdFunction a =
    25 |>
    try
        this.SecondFunction
    with
    | :? SecondLevelException as ex -> raise (new ThirdLevelException("three", ex))
事情变得很奇怪:看起来ThirdFunction中的模式匹配不再起作用,并且SecondLevelException一直传播到ThirdFunction调用者,而不包含在ThirdLevelException中。

我确信有一个合乎逻辑的解释是我的C#变形的头脑看不到。有人可以解释一下吗?

1 个答案:

答案 0 :(得分:7)

您描述的行为是正确的 - 当您编写25 |> expr时,将评估expr中的代码,然后使用25作为参数调用结果(函数)。

在您的情况下,expr的结果是一个函数,表达式的评估(返回函数)受try块的保护。但是,一旦返回该函数,它将转义try块,并在异常处理程序之外进行调用。

要在这个返回的函数中移动异常处理,你必须写这样的东西:

25 |> (fun n ->
  try 
    // You need to call the function (i.e. give it something as an argument)
    // inside the try-with block, otherwise it won't be executed here!
    this.SecondFunction n
  with 
  | :? SecondLevelException as ex -> raise (new ThirdLevelException("three", ex)))

在实践中没有人会写这种代码,但我希望它能证明这个问题!

顺便说一句:我想这与您之前关于处理管道中异常的SO问题有关。我added answer there,可以帮助您理解问题。 (问题是try .. with中管道的包装操作不会阻止流水线函数中发生的异常。)