Optimal way to use null conditional operators in boolean expressions

You are writing a boolean expression that might look like this:

team.Category == "A Team" && team?.Manager?.IsVietnamVet

public class Manager
{
    public bool IsVietnamVet { get; set; }
}

public class Team
{
    public string Category { get; set; }

    public Manager Manager { get; set; }
}

…and you get an error:

Operator ‘&&’ cannot be applied to operands of type ‘bool’ and ‘bool?’

What is the optimal/cleanest way to handle it?

  1. team.Category == "A Team" && (team?.Manager?.IsVietnamVet ?? false)

    Is that really readable?

  2. team.Category == "A Team" && (team?.Manager?.IsVietnamVet).GetValueOrDefault()

    It may not work in LINQ-to-Entities…

  3. team.Category == "A Team" && team?.Manager?.IsVietnamVet == true

    Would you really write if (condition == true) without any hesitation?

Are there any other options? Is it ultimately better to write:

  1. team.Category == "A Team" && team.Manager != null && team.Manager.IsVietnamVet

11

In this particular case, it might be wise to follow the Law of Demeter i.e.

public class Team
{
    public bool IsManagerVietnamVet => Manager?.IsVietnamVet ?? false;
}    

More generally, if a boolean expression is complex or ugly then there’s nothing to say you couldn’t divide it (or part of it) into a separate statement:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

if (team.Category == "A Team" && isVietnamVet)

When stepping through code in the debugger, it’s often nicer to have elaborate conditions packaged up into a single bool just to save a little bit of mouseover-hovering; in fact, it might just be nicer to put the whole thing in a bool variable.

bool isVietnamVetAndCategoryA = (team.Category == "A Team"
    && Manager?.IsVietnamVet ?? false);

if (isVietnamVetAndCategoryA)

or with LINQ:

var wibble = from flight in airport
             from passenger in flight.manifest
             let isOnPlane = 
                 (flight.FinishedBoarding && passenger.Flight == flight.FlightNumber)
             where !isOnPlane
             select passenger;

5

I think option 3 (i.e. == true) is the cleanest way to test that a bool? is true, because it’s very clear about what it does.

In most code x == true doesn’t make sense, because it’s the same as x, but that does not apply here, so I think == true wouldn’t be very confusing.

2

Expanding on Ben Cottrell’s answer, the “Null Object” pattern can help you further.

Instead of returning a null team/manager, extract ITeam and IManager interfaces and return meaningful alternative implementations:

public class NoManager : IManager
{
    public bool IsVietnamVet => false;
}

public class NoTeam : ITeam
{
    public bool ManagedByVietnamVet => false;

    public IManager Manager => new NoManager();
}

Then all of a sudden you can do team.ManagedByVietnamVet safely.

This of course relies on the upstream provider of team to be null-safe – but that can be ensured with appropriate testing.

0

I wrote a simple class you could use:

 public class MyBool 
    {
        public bool? Value { get; set; }

        public MyBool(bool b)
        {
            Value = b;
        }

        public MyBool(bool? b)
        {
            Value = b;
        }

        public static implicit operator bool(MyBool m)
        {
            return m?.Value ?? false;
        }

        public static implicit operator bool?(MyBool m)
        {
            return m?.Value;
        }

        public static implicit operator MyBool(bool m)
        {
            return new MyBool(m);
        }

        public static implicit operator MyBool(bool? m)
        {
            return new MyBool(m);
        }

        public override string ToString()
        {
            return Value.ToString();
        }
    }

If reliable to use a custom type, of course.
You can compare MyBool with both bool and Nullable<bool>.

1

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *