Active Patterns 可用于验证和转换函数参数
在 F#
中有趣但非常未知的 Active Patterns 用法是它们可用于验证和转换函数参数。
考虑进行参数验证的经典方法:
// val f : string option -> string option -> string
let f v u =
let v = defaultArg v "Hello"
let u = defaultArg u "There"
v + " " + u
// val g : 'T -> 'T (requires 'T null)
let g v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v.ToString ()
通常,我们在方法中添加代码以验证参数是否正确。在 F#
中使用 Active Patterns 我们可以概括它并在参数声明中声明 intent。
以下代码等同于上面的代码:
let inline (|DefaultArg|) dv ov = defaultArg ov dv
let inline (|NotNull|) v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v
// val f : string option -> string option -> string
let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u
// val g : 'T -> string (requires 'T null)
let g (NotNull v) = v.ToString ()
对于函数 f
和 g
的用户,两个不同版本之间没有区别。
printfn "%A" <| f (Some "Test") None // Prints "Test There"
printfn "%A" <| g "Test" // Prints "Test"
printfn "%A" <| g null // Will throw
值得关注的是 Active Patterns 是否会增加性能开销。让我们使用 ILSpy
来反编译 f
和 g
以查看是否是这种情况。
public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
{
return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
}
public static string g<a>(a _arg1) where a : class
{
if (_arg1 != null)
{
a a = _arg1;
return a.ToString();
}
throw new NullReferenceException();
}
感谢 inline
,与经典的参数验证方法相比,Active Patterns 不会增加额外的开销。