开发者

F# Exception Not Being Caught Correctly

开发者 https://www.devze.com 2023-02-20 00:35 出处:网络
I have an F# exception that it is not being caught in the correct catch block. Here\'s the relevant code:

I have an F# exception that it is not being caught in the correct catch block.

Here's the relevant code:

exception ConfigFileVersionIncompatabilityException of string

[<XmlType("config")>]
type Configuration() = class

    let thisVersion : string = "1.0"
    let mutable fileVersion : string = thisVersion

    [<XmlAttribute("version")>]
    member x.FileVersion
        with get() = fileVersion
        and set v = if v <> thisVersion
                    then raise (ConfigFileVersionIncompatabilityException(String.Format("Was expecting version {0} but read version {1}.", thisVersion, v)))
end


module FilterFileFunctions =

    let sampleConfigFilename = "sample.filters"

    let readConfig (file : string) =
        try
            use xmlDoc = new StreamReader(file) in
                let s = XmlSerializer(typeof<Configuration>)
                s.Deserialize(xmlDoc) :?> Configuration |> Success
        with
        | ConfigFileVersionIncompatabilityException(s) ->
            String.Format("Failed to read the configuration file: \"{0}\";\nThe following reason was given:\n{1}", file, s)
            |> Failure
        | ex ->
            String.Fo开发者_如何学运维rmat("Failed to read the configuration file: \"{0}\";\n{1}", file, ex)
            |> Failure

The problem is that the ex catch block catches the ConfigFileVersionIncompatabilityException exception, where it should be caught by the first block.

I tried to use :? System.Exception as ex instead of just ex and it still behaved the same.

Am I missing something?

[Edited 1 minute after initial post to remove irrelevant code.]


When an exception occurs during the deserialization, the Deserialize method will catch it and wrap it inside InvalidOperationException. This means that you need to chatch InvalidOperationException and then analyze the InnerException property to get to your user-defined exception.

try // ..
with 
| :? InvalidOperationException as invOp ->
   match inv.InnerException with 
   | :? ConfigFileVersionIncompatabilityException as e -> 
     printfn "%s" e.Data0
   | _ -> // generic handler
| e -> // generic handler

The Data0 property exposes the value carried by the exception (I used it, because you cannot access it easily in the pattern matching when using :?). However, you can avoid the ugly nesting of match expressions (and the duplication of generic handlers) using active patterns:

let (|InnerException|) (e:exn) =
    e.InnerException

try // ..
with 
| InnerException(ConfigFileVersionIncompatabilityException s) -> 
   printfn "%s" s
| _ -> // generic handler
0

精彩评论

暂无评论...
验证码 换一张
取 消