Избегайте нулевой проверки с помощью лямбды

В этой статье следует избегать нулевых проверок, заменив искатели счетчиками. Автор приводит пример Ruby, чтобы избежать проверки на ноль: если объект возвращается, блок запускается, если нет, то это не так.

 data_source.person(id) do |person|
  person.phone_number = phone_number
  data_source.update_person person
end

Я хотел бы сделать то же самое в C# с использованием лямбда-функции, но у меня возникли проблемы при создании примера, который делает то же самое. Не могли бы вы создать фабрику объектов, которая бы принимала номер идентификатора и лямбда-функцию?

2 ответа

Решение

Ну, я не знаю Ruby и не понимаю точный приведенный пример, но я подозреваю, что это будет что-то вроде:

dataSource.Update(id, person => person.PhoneNumber = phoneNumber);

куда DataSource.Update было бы:

  • Иметь подпись что-то вроде void Update(string id, Action<Person> updateAction (или, возможно, вернуть bool указать, нашел ли он человека)
  • Быть реализованным как:
    • Найти человека с данным ID
    • Если он не существует, немедленно вернитесь
    • В противном случае выполните данное действие и обновите хранилище резервных копий с измененным объектом.

Или в более общем смысле (и ближе к оригинальному Ruby):

dataSource.WithPerson(id, person => {
    person.PhoneNumber = phoneNumber;
    dataSource.UpdatePerson(person);
};

Лично я предпочитаю первую форму: она более конкретна в отношении того, чего она пытается достичь, но она вполне может подойти к лучшей реализации, и она, безусловно, чище в вызывающем коде.

Альтернативным подходом было бы использование монады Maybe.

Это позволит вам сохранить существующий API как есть, то есть вы все еще можете иметь dataSource.GetPersonById(id),

Код, который использует монаду Maybe, выглядит следующим образом:

dataSource.GetPersonById(id)
          .Maybe()
          .Do(person => {
                            person.PhoneNumber = phoneNumber;
                            dataSource.UpdatePerson(person);
                        });

Чтобы иметь возможность использовать монаду Maybe, вам потребуется код в следующем абзаце.
Основой для этого послужил код из ссылки в блоге Дэниела Эрвикера.
Я расширил его, чтобы добавить Maybe метод расширения и сделал его компиляцией.

public struct MaybeMonad<T> where T : class
{
    private readonly T _value;

    public MaybeMonad(T value)
    {
        _value = value;
    }

    public MaybeMonad<TResult> Select<TResult>(Func<T, TResult> getter)
        where TResult : class
    {
        var result = (_value == null) ? null : getter(_value);
        return new MaybeMonad<TResult>(result);
    }  

    public TResult Select<TResult>(Func<T, TResult> getter,
                                   TResult alternative)
    {
        return (_value == null) ? alternative : getter(_value);
    }

    public void Do(Action<T> action)
    {
        if (_value != null)
            action(_value);
    }
}

public static class Maybe
{
    public static MaybeMonad<T> From<T>(T value) where T : class
    {
        return new MaybeMonad<T>(value);
    }
}

public static class MaybeMonadExtensions
{
    public static MaybeMonad<T> Maybe<T>(this T value) where T : class
    {
        return new MaybeMonad<T>(value);
    }
}
Другие вопросы по тегам