Modo ottimale per utilizzare operatori condizionali nulli nelle espressioni booleane


11

Stai scrivendo un'espressione booleana che potrebbe apparire così:

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

... e ricevi un errore:

L'operatore "&&" non può essere applicato agli operandi di tipo "bool" e "bool?"

Qual è il modo ottimale / più pulito per gestirlo?

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

    È davvero leggibile?

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

    Potrebbe non funzionare in LINQ-to-Entities ...

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

    Scriveresti davvero if (condition == true)senza alcuna esitazione?

Ci sono altre opzioni? Alla fine è meglio scrivere:

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

if (! (String.IsNullOrEmpty (team.Manager) && condition)) ...
Snoop

1
@StevieV è stato come essere così fin dall'inizio;)
Santhos,

1
Osservando il codice con più attenzione, la parte condizionale nulla dovrebbe essere team.Manager?.IsVietnamVet, cioè nessuna condizione condizionale nulla dopo team, poiché già non può esserlo null.
svick

2
"Scriveresti davvero se (condizione == vero) senza alcuna esitazione?" Per un normale booleano, no, poiché è superfluo. Tuttavia, per un valore booleano nullable , questo è rilevante. nullableBool == truesta fondamentalmente testando nullableBool != false && nullableBool != null(ed è questa seconda parte che lo rende utile e quindi non superfluo)
Flater

2
Ho iniziato a utilizzare nullableBool == truese non creo un wrapper. È leggibile perché normalmente non si scrive == truequando si usa il normale booleano come menziona @Flater, quindi suggerisce che la variabile è nullable. Adizionale, migliora la leggibilità di LINQ perché non usi più condizioni null come menziona @Fabio.
Santhos,

Risposte:


4

In questo caso particolare, potrebbe essere saggio seguire la Legge di Demetra, cioè

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

Più in generale, se un'espressione booleana è complessa o brutta, non c'è nulla da dire che non puoi dividerla (o parte di essa) in un'istruzione separata:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

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

Quando si passa attraverso il codice nel debugger, è spesso più bello avere condizioni elaborate raggruppate in un singolo boolsolo per risparmiare un po 'di passaggio del mouse; in effetti, potrebbe essere più bello mettere il tutto in una boolvariabile.

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

if (isVietnamVetAndCategoryA)

o con LINQ:

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

Penso che per lo più mi sto orientando verso una soluzione del genere.
Santhos,

1
Nuova sintassi C # per salvare alcune righe: bool pubblico IsManagerVietnamVet => (team.Category == "") && (team.Manager? .IsVietnamVet ?? false);
Graham,

Tieni presente che i long a && b && c &&...vengono eseguiti in sequenza e il successivo non viene nemmeno eseguito se il precedente lo è false. Quindi la gente di solito mette il più veloce all'inizio. Tienilo a mente quando sposti cose in un bool-var separato
jitbit

@jitbit Riguarda maggiormente la manutenibilità. Le microottimizzazioni come quella che menzioni sono generalmente inutili: non vale la pena preoccuparsi a meno che le prestazioni non siano identificate come un problema e la profilazione mostri che apportare modifiche del genere avrebbe un impatto evidente.
Ben Cottrell,

@BenCottrell Lo definirei un'ottimizzazione "micro" se la IsManagerVietnamVetproprietà va a controllare in un database;)
jitbit

3

Credo che l'opzione 3 (cioè == true) è il modo più pulito per un test che bool?è true, perché è molto chiaro su quello che fa.

Nella maggior parte del codice x == truenon ha senso, perché è lo stesso di x, ma non si applica qui, quindi penso == trueche non sarebbe molto confuso.


1
Penso che sarebbe sicuramente la strada da percorrere se volessimo usare l'operatore condizionale null, perché è anche linq sicuro. La domanda è se la leggibilità è buona. Dal mio punto di vista, la scelta è tra opt 3 e opt 4 e sceglierei 4 su 3 per motivi di leggibilità, anche l'intenzione del programmatore sembra un po 'più chiara. Ho contrassegnato la risposta di Ben Cottrell come corretta perché mi piace un po 'di più questo approccio. Se potessi segnare due risposte, lo farei.
Santhos,

1
@Santhos - re "l'intenzione del programmatore sembra un po 'più chiara". IMHO, questo è un caso in cui la prima volta che un programmatore vede a?.b == trueche saranno confusi, ma una volta capito cosa sta facendo, diventa un linguaggio molto leggibile, molto più bello del dover testare != nullnel mezzo di un'espressione complessa. Lo stesso per a?.b ?? false, che sembra aver guadagnato la trazione come la soluzione più popolare, perché corrisponde a ciò che si scriverebbe se si trattasse di un tipo diverso da booleano [anche se non mi piace per il booleano; per me, non legge come naturalmente; Io preferisco == true].
ToolmakerSteve

3

Espandendo la risposta di Ben Cottrell, il modello "Null Object" può aiutarti ulteriormente.

Invece di restituire un nullteam / manager, estrai ITeame IManagerinterfacce e restituisci implementazioni alternative significative:

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

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

    public IManager Manager => new NoManager();
}

Quindi all'improvviso puoi fare in team.ManagedByVietnamVetsicurezza.

Questo ovviamente si basa sul fatto che il fornitore a monte teamsia sicuro per nulla, ma ciò può essere garantito con test adeguati.


-4

Ho scritto una classe semplice che potresti usare:

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

Se affidabile per usare un tipo personalizzato, ovviamente. Puoi confrontare MyBoolcon entrambi boole Nullable<bool>.


1
Questo sito è dedicato a domande sulla progettazione del software piuttosto che su come far funzionare specifici pezzi di codice, quindi se ritieni che questa sia la soluzione migliore, la tua risposta dovrebbe concentrarsi sul motivo per cui ritieni che una classe come questa sia la migliore soluzione possibile, non il codice esatto necessario per implementarlo.
Ixrec,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.