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


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 для юридичних осіб ...

  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) && condition)) ...
Snoop

1
@StevieV це було так, як це було з самого початку;)
Сантос

1
Дивлячись на код уважніше, нульовою умовною частиною має бути team.Manager?.IsVietnamVet, тобто не бути після цього умовною team, оскільки вже не може бути null.
svick

2
"Ви б справді написали, якщо (умова == вірно) без вагань?" Для звичайного булевого - ні, оскільки це зайве. Однак для нульового булевого значення це актуально. nullableBool == trueв основному є тестуванням на nullableBool != false && nullableBool != null(а саме ця друга частина робить її корисною і тому не зайвою)
Flater

2
Я почав використовувати, nullableBool == trueякщо я не створю обгортку. Він читабельний, оскільки ви, як правило, не пишете == trueпід час використання звичайного булевого тексту, як згадує @Flater, тому це дозволяє припустити, що змінна є нульовою. Крім того, це покращує читабельність 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;

Я думаю, що я переважно схиляюся до такого рішення.
Сантос

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

Просто майте на увазі, що довгі a && b && c &&...виконуються послідовно, а наступні навіть не виконуються, якщо є попередніми false. Тож люди зазвичай ставлять найшвидше на початку. Майте це на увазі під час переміщення матеріалів в окремий bool-var
jitbit

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

@BenCottrell Я б назвав це "мікро" оптимізацією, якщо IsManagerVietnamVetвластивість перейде на перевірку в базі даних;)
jitbit

3

Я думаю, що варіант 3 (тобто == true) - це найчистіший спосіб перевірити, що це bool?таке true, оскільки це дуже ясно, що він робить.

У більшості кодів x == trueнемає сенсу, тому що він такий самий, як x, але це не стосується тут, тому я думаю, це == trueбуло б не дуже заплутано.


1
Я думаю, що це, безумовно, було б шляхом, якби ми хотіли використовувати нульовий умовний оператор, тому що це також безпечно для linq. Питання в тому, чи добре читабельність. З моєї точки зору, вибір між опти-3 та опти-4, і я вибрав би 4 над 3 з причини читабельності, також намір програміста здається дещо чіткішим. Я позначив відповідь Бен Коттрелла як правильну, тому що мені подобається такий підхід трохи більше. Якби я міг позначити дві відповіді, я би.
Сантос

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
Цей сайт призначений для запитань щодо розробки програмного забезпечення, а не як змусити конкретні фрагменти коду працювати, тому якщо ви вважаєте, що це найкраще рішення, тоді ваша відповідь повинна зосередитися на тому, чому ви вважаєте, що такий клас як найкраще рішення, а не точний код, необхідний для його реалізації.
Іксрек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.