让我们考虑一下这段代码:
let getBuildDate (assembly: Assembly) : DateTime option =
let buildVersionMetadataPrefix = "+build"
let attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
if attribute <> null && attribute.InformationalVersion <> null then
let value = attribute.InformationalVersion
let index = value.IndexOf(buildVersionMetadataPrefix)
if index > 0 then
let value = value.Substring(index + buildVersionMetadataPrefix.Length)
let success, timestamp = DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None)
if success then
Some timestamp
else
None
else
None
else
None
Run Code Online (Sandbox Code Playgroud)
有没有办法摆脱所有的“else None”语句而只有一个?
一方面,我可以想象,对于某些人来说,代码更清晰,所有 None 语句都拼出来了,但另一方面,来自 C 世界,我认为它是混乱的,降低了可读性。
在很多情况下,您需要满足一系列条件,并且所有失败的案例都集中在一个地方。
如果我有一个依赖于彼此成功的条件列表,我怎样才能做到简洁的短退出而不重复。
另一种方法可能是使用这些Option函数 - 如果上一步的输入是 ,那么这些步骤中的每一步都会有效地短路None。
let getBuildDate (assembly: Assembly) : DateTime option =
let tryDate value =
match DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None) with
| true, date -> Some date
| false, _ -> None
let buildVersionMetadataPrefix = "+build"
let attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
Option.ofObj attribute
|> Option.bind (fun attr -> Option.ofObj attr.InformationalVersion)
|> Option.map (fun infVer -> infVer, infVer.IndexOf buildVersionMetadataPrefix)
|> Option.filter (fun (_, index) -> index > 0)
|> Option.map (fun (infVer, index) -> infVer.Substring(index + buildVersionMetadataPrefix.Length))
|> Option.bind tryDate
Run Code Online (Sandbox Code Playgroud)
这是否“更好”是有争议的——而且绝对是一个见仁见智的问题!
其他答案展示了如何使用更复杂的函数式编程方法来做到这一点,比如使用计算表达式或option值。如果您在整个系统的许多地方都这样做,这些绝对有用并且有意义。
但是,如果您只是想要一种简单的方法来更改代码,从而使控制流更加清晰(而不是让它更聪明),我会否定这些条件。以前,您拥有:
if something then
moreStuff()
Some result
else
None
Run Code Online (Sandbox Code Playgroud)
您可以通过返回Noneif来重写它not something。我认为这种情况下的 F# 编码约定还允许您删除缩进,因此它看起来更像是命令式提前返回:
if not something then None else
moreStuff()
Some result
Run Code Online (Sandbox Code Playgroud)
有了这个,您可以按如下方式编写原始函数 - 无需任何额外的巧妙技巧:
let getBuildDate (assembly: Assembly) : DateTime option =
let buildVersionMetadataPrefix = "+build"
let attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
if attribute = null || attribute.InformationalVersion = null then None else
let value = attribute.InformationalVersion
let index = value.IndexOf(buildVersionMetadataPrefix)
if index <= 0 then None else
let value = value.Substring(index + buildVersionMetadataPrefix.Length)
let success, timestamp = DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None)
if not success then None else
Some timestamp
Run Code Online (Sandbox Code Playgroud)