为何F#中字符串类型的模式匹配会匹配ReadOnlySpan<char>?
过滤uint64.TryParse方法的ReadOnlySpan版本方案
问题背景
我要找uint64类型中从string解析的TryParse方法,但运行代码后返回了两个结果:一个以string为首个参数,另一个以ReadOnlySpan<char>为首个参数。由于F#里临时创建ReadOnlySpan比较麻烦,我需要修改代码中的模式匹配,只保留前者。
原代码及输出:
typeof<uint64>.GetMember("TryParse") |> Seq.cast<MethodBase> |> Seq.choose (fun m -> let parameterTypes = m.GetParameters() |> Array.map (fun p -> (p.ParameterType, p.Attributes)) // [1] 需要修改的模式匹配位置 match parameterTypes with | [| (string, ParameterAttributes.None); (uint64, ParameterAttributes.Out) |] -> Some m | _ -> None) |> Seq.toList |> List.map (fun m -> (m.Name, m.GetParameters() |> Array.map (fun p -> p.ParameterType.FullName))) (* 输出结果: val it: (string * string array) list = [("TryParse", [|"System.String"; "System.UInt64&"|]); ("TryParse", [|"System.ReadOnlySpan`1[[System.Char, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]"; "System.UInt64&"|])] *)
问题根源
原代码的模式匹配存在逻辑错误:(string, ...)是将参数类型绑定到名为string的变量,而不是匹配System.String类型。这导致任何类型都会被匹配到,所以两个TryParse方法都通过了筛选条件。
解决方案
修改模式匹配逻辑,用守卫条件明确限定第一个参数的类型必须是typeof<string>,同时保留对out参数的判断:
修改后的完整代码
typeof<uint64>.GetMember("TryParse") |> Seq.cast<MethodBase> |> Seq.choose (fun m -> let parameterTypes = m.GetParameters() |> Array.map (fun p -> (p.ParameterType, p.Attributes)) // 修改后的模式匹配 match parameterTypes with | [| (t, ParameterAttributes.None) when t = typeof<string>; (_, ParameterAttributes.Out) |] -> Some m | _ -> None) |> Seq.toList |> List.map (fun m -> (m.Name, m.GetParameters() |> Array.map (fun p -> p.ParameterType.FullName)))
更严谨的版本(可选)
如果需要同时校验第二个参数的类型是uint64的引用类型(out参数本质是引用类型),可以用以下模式匹配:
match parameterTypes with | [| (t, ParameterAttributes.None) when t = typeof<string>; (tOut, ParameterAttributes.Out) when tOut = typeof<uint64>.MakeByRefType() |] -> Some m | _ -> None
修改后运行代码,就只会返回以string为首个参数的TryParse方法了。
内容的提问来源于stack exchange,提问作者7enderhead
相关产品推荐
相关产品推荐

