Оптимальный способ использования нулевых условных операторов в булевых выражениях


11

Вы пишете логическое выражение, которое может выглядеть так:

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; }
}

... и вы получите ошибку:

Оператор '&&' нельзя применять к операндам типа 'bool' и 'bool?'

Какой оптимальный / самый чистый способ справиться с этим?

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

    Это действительно читабельно?

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

    Это может не работать в LINQ-to-Entities ...

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

    Вы действительно пишете if (condition == true)без каких-либо колебаний?

Есть ли другие варианты? В конечном итоге лучше написать:

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

if (! (String.IsNullOrEmpty (team.Manager) && условие)) ...
Snoop

1
@StevieV С самого начала я хотел быть таким;)
Santhos

1
Если присмотреться к коду более внимательно, нулевая условная часть должна быть team.Manager?.IsVietnamVet, т.е. не иметь нулевой условной части после team, поскольку уже не может быть null.
svick

2
"Не могли бы вы написать, если (условие == правда) без каких-либо колебаний?" Для нормального логического значения нет, так как это лишнее. Тем не менее, для обнуляемого логического значения это актуально. nullableBool == trueв основном тестирует nullableBool != false && nullableBool != null(и именно эта вторая часть делает его полезным и, следовательно, не лишним)
Flater

2
Я начал использовать, nullableBool == trueесли я не создаю обертку. Это читабельно, потому что вы обычно не пишете == trueпри использовании обычного логического значения, как упоминает @Flater, поэтому он предполагает, что переменная имеет значение null. Кроме того, это улучшает читаемость LINQ, потому что вы не используете несколько нулевых условий, как упоминает @Fabio.
Сантос

Ответы:


4

В данном конкретном случае было бы целесообразно следовать закону Деметры, т.е.

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

В более общем смысле, если логическое выражение является сложным или уродливым, тогда нечего сказать, что вы не могли бы разделить его (или его часть) на отдельный оператор:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

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

При пошаговом прохождении кода в отладчике часто бывает лучше, чтобы сложные условия были объединены в один, boolчтобы сэкономить немного при наведении курсора мыши; на самом деле, может быть, лучше поместить все это в boolпеременную.

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

if (isVietnamVetAndCategoryA)

или с LINQ:

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

Я думаю, что в основном склоняюсь к такому решению.
Santhos

1
Новый синтаксис C # для сохранения нескольких строк: public bool IsManagerVietnamVet => (team.Category == "") && (team.Manager? .IsVietnamVet ?? false);
Грэм

Просто имейте в виду, что long a && b && c &&...выполняются последовательно, а следующий даже не выполняется, если предыдущий false. Так что люди обычно ставят быстрее всех в начале. Имейте это в виду, когда перемещаете вещи в отдельный bool-var
jitbit

@jitbit Это больше о ремонтопригодности. Микрооптимизации, подобные той, которую вы упоминаете, как правило, не нужны - не стоит беспокоиться, если производительность не будет определена как проблема, а профилирование не покажет, что внесение подобных изменений будет иметь заметное влияние.
Бен Коттрелл

@BenCottrell Я бы назвал это «микро» оптимизацией, если IsManagerVietnamVetсвойство проверяется в базе данных;)
jitbit

3

Я думаю, что вариант 3 (т. Е. == true) Является самым чистым способом проверки того, что bool?есть true, потому что он очень четко понимает, что он делает.

В большинстве случаев код x == trueне имеет смысла, потому что он такой же, как и здесь x, но это не применимо, так что я думаю, что == trueэто не будет очень запутанным.


1
Я думаю, что это определенно было бы правильным способом, если бы мы хотели использовать условный оператор null, потому что он также безопасен для linq. Вопрос в том, хорошая ли читаемость? С моей точки зрения, выбор между опцией 3 и опцией 4, и я бы выбрал 4 из 3 по причине читабельности, также намерение программиста кажется немного более ясным. Я отметил ответ Бена Коттрелла как правильный, потому что мне нравится этот подход немного больше. Если бы я мог отметить два ответа, я бы.
Santhos

1
@Santhos - «Намерение программиста кажется более ясным». ИМХО, это тот случай, когда программист в первый раз видит, a?.b == trueчто он будет сбит с толку, но как только он поймет, что он делает, он станет очень читабельным идиомой, гораздо приятнее, чем тестирование != nullв середине сложного выражения. То же самое для a?.b ?? false, которое, кажется, получило тягу как самое популярное решение, потому что оно соответствует тому, что вы набрали бы, если бы вы имели дело с типом, отличным от логического [хотя мне не нравится это для логического; для меня это не читается так естественно; Я предпочитаю == true].
ToolmakerSteve

3

Продолжая ответ Бена Коттрелла, шаблон «Нулевой объект» может помочь вам в дальнейшем.

Вместо того, чтобы возвращать nullкоманду / менеджера, извлекать ITeamи IManagerвзаимодействовать и возвращать значимые альтернативные реализации:

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

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

    public IManager Manager => new NoManager();
}

Тогда вдруг вы можете сделать это team.ManagedByVietnamVetбезопасно.

Это, конечно, полагается на то, что вышестоящий провайдер teamдолжен быть нулевым, но это может быть обеспечено с помощью соответствующего тестирования.


-4

Я написал простой класс, который вы могли бы использовать:

 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();
        }
    }

Если надежно использовать пользовательский тип, конечно. Вы можете сравнить MyBoolс обоими boolи Nullable<bool>.


1
Этот сайт предназначен для вопросов о дизайне программного обеспечения, а не о том, как заставить работать определенные кусочки кода, поэтому, если вы считаете, что это лучшее решение, тогда ваш ответ должен быть сосредоточен на том, почему вы считаете, что такой класс является наилучшим возможным решением, а не точный код, необходимый для его реализации.
Ixrec
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.