Это кандидат на вычислительные выражения? - PullRequest
5 голосов
/ 09 мая 2011

У меня есть следующий код 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);
}

Я хотел бы отметить, что API ISecurityService в настоящее время возвращает false, если какой-либо из элементовявляются нулевымиОднако он выполняет вызов базы данных, поэтому код здесь проверяет наличие нуля, а затем выполняет проверку идентификатора, поскольку в большинстве случаев он возвращает false и избегает вызова базы данных.

Ответы [ 2 ]

8 голосов
/ 09 мая 2011

Вы можете определить конструктор вычислений, который скрывает проверку 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 это просто боль :-).В этой статье Иена Гриффитса есть некоторые связанные идеи, но, опять же, ни одна из них действительно не решает проблему.

1 голос
/ 09 мая 2011

Я бы сделал одну небольшую поправку к ответу Томаса: используйте Object.ReferenceEquals для проверки нуля вместо =. Это быстрее и, что более важно, вам не нужно отмечать типы, объявленные в F #, атрибутом AllowNullLiteral. Я обычно определяю Interop модуль для кода F #, который будет использоваться из C #. Это изолирует обработку нуля, и, поскольку она не требует использования [<AllowNullLiteral>], вы можете игнорировать нуль в F # и иметь дело с ним только в точке взаимодействия с C # (т. Е. Вашим открытым интерфейсом). Вот модуль, который я использую (скопировано с этот ответ ):

[<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.

...