Question

In this article Avoid Null Checks by replacing finders with tellers the author gives a Ruby example to avoid null checking, if the object is returned the block is run, if not then it isn't.

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

I'd like to do the same thing in C# using a lambda function but am having trouble coming up with an example that does the same type of thing. Would you create the object factory to accept the id number and also a lambda function?

Was it helpful?

Solution

Well I don't know Ruby and don't understand the exact example given, but I suspect it would be something like:

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

Where DataSource.Update would:

  • Have a signature of something like void Update(string id, Action<Person> updateAction (or possibly return a bool to indicate whether or not it found the person)
  • Be implemented as:
    • Find the person with the given ID
    • If it doesn't exist, return immediately
    • Otherwise, execute the given action, and update the backing store with the modified object

Or more generally (and closer to the original Ruby):

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

Personally I prefer the first form: it's more specific about what it's trying to achieve, but that may well lend itself to a better implementation, and it's certainly cleaner in the calling code.

OTHER TIPS

An alternative approach would be to use the Maybe monad.

This would allow you to keep your existing API as is, i.e. you can still have a dataSource.GetPersonById(id).

Code that uses the Maybe monad looks like this:

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

To be able to use the Maybe monad you would need to have the code in the following paragraph.
The base for it has been the code from the linked blog post by Daniel Earwicker.
I extended it to add the Maybe extension method and made it compile.

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);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top