我有以下c#代码,它会检查权限。我想知道,当转换为f#时,计算表达式是否是一种分解空检查的方法。
bool ShouldGrantPermission(ISecurityService security, User user, Item item) {
return user != null && item != null && user.Id == item.AuthorId
&& security.Does(user).Have(MyPermission).On(item);
}
我想要注意的是,如果任何项为null,则ISecurityService API当前返回false。但它会进行数据库调用,因此这里的代码检查null然后执行id检查,因为在大多数情况下,这将返回false并避免数据库调用。
答案 0 :(得分:8)
您可以定义一个隐藏null
检查的计算构建器,但它不会为您提供非常方便的语法,因此我可能不会这样写。如果有一些更轻量级的语法,那将是很酷的,因为它会非常有用。此外,计算构建器只传播null
,因此您将以Nullable<bool>
类型的结果结束:
nullable { let! u = user
let! i = item
return u.Id == i.AuthorId && security.Does(user).Have(MyPermission).On(i) }
这个想法是let!
操作仅在参数不是null
时调用其余的计算。当它为null
时,它会立即返回null
作为整体结果。
我认为你没有太多办法让代码变得更好。当然,如果它都是用F#编写的,那么这些值都不能是null
(因为F#声明的类型不允许null
值),但这是一个不同的故事。
F#中的另一种方法是声明仅在值不是null
时才匹配的活动模式。这样做的好处是,您不会在代码中包含任何可能具有null
值的变量,因此不存在使用错误变量并获取NullReferenceException
的危险:
let shouldGrantPermission = function
| NotNull(security:ISecurityService), NotNull(user), NotNull(item) ->
security.Does(user).Have(MyPermission).On(item)
| _ -> true
活动模式的声明是:
let (|NotNull|_|) a = if a <> null then Some(a) else None
然而,即使这并不比你拥有的东西更直接。我想处理null
值只是痛苦:-)。这个article by Ian Griffiths有一些相关的想法,但同样,它们都没有真正解决问题。
答案 1 :(得分:1)
我会对Tomas的答案做一点调整:使用Object.ReferenceEquals
进行空检查而不是=
。它更快,更重要的是,您不必使用AllowNullLiteral
属性标记在F#中声明的类型。我通常为将从C#使用的F#代码定义一个Interop
模块。这隔离了空处理,并且由于它不需要使用[<AllowNullLiteral>]
,因此您可以忽略F#中的null并且仅在与C#(即您的公共接口)交互时处理它。这是我使用的模块(从this answer复制):
[<AutoOpen>]
module Interop =
let inline (===) a b = obj.ReferenceEquals(a, b)
let inline (<=>) a b = not (a === b)
let inline isNull value = value === null
let inline nil<'T> = Unchecked.defaultof<'T>
let inline safeUnbox value = if isNull value then nil else unbox value
let (|Null|_|) value = if isNull value then Some() else None
type Foo() = class end
type Test() =
member this.AcceptFoo(foo:Foo) = //passed from C#
if isNull foo then nullArg "foo"
else ...
member this.AcceptFoo2(foo:Foo) = //passed from C#
match foo with
| Null -> nullArg "foo"
| _ -> ...
member this.AcceptBoxedFoo(boxedFoo:obj) =
let foo : Foo = safeUnbox boxedFoo
...
member this.ReturnFoo() : Foo = //returning to C#
if (test) then new Foo()
else nil
fssnip.net上的