目前我们这样做......
let parseDate defaultVal text =
match DateTime.TryParse s with
| true, d -> d
| _ -> defaultVal
是否可以这样做......
let d : DateTime = tryParse DateTime.MinValue "2015.05.01"
答案 0 :(得分:10)
是。欢迎来到成员约束,ref和byref值的世界。
let inline tryParseWithDefault
defaultVal
text
: ^a when ^a : (static member TryParse : string * ^a byref -> bool)
=
let r = ref defaultVal
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r.contents))
then !r
else defaultVal
defaultVal
和text
是形式参数,将被推断。此处,text
已被约束为string
,因为它被用作对静态方法SomeType.TryParse
的调用中的第一个参数,稍后将进行解释。^a
是静态解析的类型参数(与'a
形式的泛型类型参数相对)。 ^a
将在编译时解析为特定类型。因此,托管它的函数必须标记为inline
,这意味着函数的每次调用都将成为函数实际主体的就地调用的替换,其中每个静态类型参数将成为特定的类型;在这种情况下,无论defaultVal
类型是什么。没有基本类型或接口类型约束限制defaultVal
的可能类型。但是,您可以提供静态和实例成员约束,例如此处所做的。具体来说,结果值(因此defaultVal
)显然必须有一个名为TryParse
的静态成员,它接受string
,对该类型的可变实例的引用,并返回boolean
值: ^a when ...
。通过在defaultVal
开头的行上规定的返回类型,明确了这种约束。 ^a
本身是可能的结果这一事实将其约束为与: ^a when ^a : (static ....
相同的类型。 (约束也隐含在整个函数的其他地方)。^a
将结果类型string * ^a byref -> bool
描述为具有名为TryParse且类型为string
的静态成员。也就是说,结果类型将具有接受boolean
的静态成员,对自身实例的引用(因此是可变的),并将返回byref
值。此描述是F#如何匹配DateTime,Int32,TimeSpan等类型的TryParse的.Net定义。请注意,out
是F#的等效于C#的ref
或let r = ref defaultVal
参数修饰符。defaultVal
创建引用类型,并将提供的值ref
复制到其中。 mutable
是F#创建可变类型的方法之一。另一个是if (^a : (static...
关键字。不同之处在于mutable将其值存储在堆栈上,而ref将其存储在主内存/堆中并保存一个地址(在堆栈上)。最新版本的F#将根据上下文自动将可变指定升级为ref,允许您仅根据可变性进行编码。if
是关于静态推断类型^a
上的TryParse方法的调用结果的(text, &r.contents)
语句。此TryParse按其(string * ^a byref)
签名传递&r.contents
。在此,r
根据TryParse的预期提供out
(模拟C#的ref
或(text, &r.contents)
参数)的可变内容的引用。注意,我们在这里没有预留,并且与.Net框架互操作的某些F#niceties没有扩展到这一点,特别是将空间分离的F#参数自动汇总到.net框架函数参数作为元组。因此,参数作为元组!r
。r.Value
是您阅读参考值的方式。 TryParse
也可以。 .Net提供的r
方法似乎总是为out参数设置一个值。因此,不严格要求默认值。但是,您需要一个结果值保持器^a
,并且它必须具有初始值,即使为null。我不喜欢null。当然,另一个选择是对需要某种默认值属性的Unchecked.defaultof< ^a >
施加另一个约束。
以下后续解决方案通过使用Option
从“推断结果”类型派生合适的占位符值来消除对默认参数的需求(是的,感觉就像魔术一样)。它还使用^a option
类型来表征获取结果值的成功和失败。因此,结果类型为tryParse
text
: ^a option when ^a : (static member TryParse : string * ^a byref -> bool)
=
let r = ref Unchecked.defaultof< ^a >
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r.contents))
then Some (!r)
else None
。
^a
而且,根据@kvb建议,以下简洁是可能的。在这种情况下,类型推断用于规定if (^a : ...))
上的类型约束,因为它在r
表达式中被调用,并且还建立了可变缓冲区let inline tryParseWithDefault defaultVal text : ^a option =
let mutable r = defaultVal
if (^a : (static member TryParse: string * ^a byref -> bool) (text, &r))
then Some r
else None
let inline tryParse text = tryParseWithDefault (Unchecked.defaultof<_>) text
的类型。 TryParse的out参数。 I have since come to learn this is how FsControl does some of it's magic
?
对于在实例成员上使用类型约束的情况,例如类型约束fsharp的动态成员查找自定义运算符FindName:string->obj
,使得主题的类型必须包含let inline (?) (instanceObj:^A) (property:string) : 'b =
(^A : (member FindName:string -> obj) (instanceObj, property)) :?> 'b
成员,语法为如下:
self
注意:
'b
对象,该对象通常是隐藏的第一个参数let button : Button = window?myButton
let report : ReportViewer = window?reportViewer1
示例用法如下:
{{1}}