Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 这是计算表达式的候选者吗?_C#_F#_Monads - Fatal编程技术网

C# 这是计算表达式的候选者吗?

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

我有下面的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并避免数据库调用

您可以定义一个隐藏
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