C# 这是计算表达式的候选者吗?
我有下面的c代码,它检查权限。我想知道,当转换为f#时,计算表达式是否是一种排除空检查的方法C# 这是计算表达式的候选者吗?,c#,f#,monads,C#,F#,Monads,我有下面的c代码,它检查权限。我想知道,当转换为f#时,计算表达式是否是一种排除空检查的方法 bool ShouldGrantPermission(ISecurityService security, User user, Item item) { return user != null && item != null && user.Id == item.AuthorId && security.Does(user).Have(MyP
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并避免数据库调用 您可以定义一个隐藏
null
检查的计算生成器,但它没有提供非常方便的语法,因此我可能不会这样编写它。如果有更轻量级的语法,那就太酷了,因为它会非常有用。此外,计算生成器只是传播null
,因此您将以类型为Nullable
的结果结束:
这个想法是,让代码>操作仅在参数不为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
let(| NotNull | | |)a=如果一个为null,那么另一些(a)则为None
然而,即使这样,也不会比你所拥有的直接等价物好多少。我想处理null
值只是一种痛苦:-)。这有一些相关的想法,但同样,没有一个真正解决问题。我会对Tomas的答案做一点小小的调整:使用对象。ReferenceEquals
执行空检查,而不是=
。它更快,而且更重要的是,您不必使用AllowNullLiteral
属性标记在F#中声明的类型。我通常为F代码定义一个Interop
模块,该模块将从C#中使用。这将隔离空处理,并且由于它不需要使用[):
[在fssnip.net上。多亏了你的回答,我现在不太担心我对单子的理解。(!=
唯一的问题是!
并不意味着“不”在F#的其他任何地方。有些人可能会称之为讨厌,但我几乎认为
将是===
@Stephen更惯用的伴侣-我对你的运算符的外观不太感兴趣,但我不能指责你的推理。很遗憾F#不支持unicode运算符!我们可以使用=•=
和
(bullet表示null
,这非常合适,因为它非常适合攻击自己的脚:-)@Tomas:“很遗憾F#不支持unicode运算符!”。阿门!我希望能够使用×,”,⊕, ⊗ 等等,还有∂ 在标识符中…@Jon-那么你也可以在你的库中出售一个特殊的键盘:-)。
let (|NotNull|_|) a = if a <> null then Some(a) else None
[<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