Cara optimal untuk menggunakan operator bersyarat nol dalam ekspresi boolean


11

Anda sedang menulis ekspresi boolean yang mungkin terlihat seperti ini:

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

... dan Anda mendapatkan kesalahan:

Operator '&&' tidak dapat diterapkan ke operan tipe 'bool' dan 'bool?'

Apa cara optimal / terbersih untuk menanganinya?

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

    Apakah itu benar-benar dapat dibaca?

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

    Ini mungkin tidak berfungsi di LINQ-to-Entities ...

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

    Apakah Anda benar-benar menulis if (condition == true)tanpa ragu-ragu?

Apakah ada opsi lain? Apakah akhirnya lebih baik menulis:

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

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

1
@StevieV itu ment menjadi seperti ini sejak awal;)
Santhos

1
Melihat kode lebih hati-hati, bagian kondisional nol seharusnya team.Manager?.IsVietnamVet, yaitu tidak ada kondisional nol setelah team, karena sudah tidak bisa null.
svick

2
"Apakah Anda benar-benar menulis jika (kondisi == benar) tanpa ragu-ragu?" Untuk boolean normal, tidak, karena itu berlebihan. Namun, untuk boolean yang dapat dibatalkan , ini relevan. nullableBool == truepada dasarnya sedang menguji nullableBool != false && nullableBool != null(dan ini bagian kedua yang membuatnya berguna dan karenanya tidak berlebihan)
Flater

2
Saya mulai menggunakan nullableBool == truejika saya tidak membuat pembungkus. Ini dapat dibaca karena Anda biasanya tidak menulis == trueketika menggunakan boolean biasa seperti @Flater menyebutkan, jadi itu menunjukkan bahwa variabel tersebut dapat dibatalkan. Secara tradisional, ini meningkatkan keterbacaan LINQ karena Anda tidak menggunakan beberapa kondisi null sebagai @Fabio menyebutkan.
Santhos

Jawaban:


4

Dalam kasus khusus ini, mungkin bijaksana untuk mengikuti Hukum Demeter yaitu

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

Lebih umum, jika ekspresi boolean kompleks atau jelek maka tidak ada yang bisa dikatakan Anda tidak bisa membaginya (atau bagian dari itu) menjadi pernyataan terpisah:

bool isVietnamVet = Manager?.IsVietnamVet ?? false;

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

Ketika melangkah melalui kode dalam debugger, seringkali lebih baik untuk memiliki kondisi rumit yang dikemas menjadi satu boolhanya untuk menyimpan sedikit mouse-melayang-layang; bahkan, mungkin lebih baik untuk meletakkan semuanya dalam sebuah boolvariabel.

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

if (isVietnamVetAndCategoryA)

atau dengan LINQ:

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

Saya pikir saya lebih condong ke arah solusi semacam itu.
Santhos

1
Sintaks C # baru untuk menyimpan beberapa baris: bool publik IsManagerVietnamVet => (team.Category == "") && (team.Manager? .IsVietnamVet ?? false);
Graham

Perlu diingat bahwa lama a && b && c &&...dieksekusi secara berurutan, dan yang berikutnya bahkan tidak dieksekusi jika yang sebelumnya dijalankan false. Jadi orang biasanya menempatkan tercepat di awal. Ingatlah hal itu ketika memindahkan barang ke bool-var yang terpisah
jitbit

@ Jitbit Ini tentang pemeliharaan. Optimalisasi mikro seperti yang Anda sebutkan pada umumnya tidak perlu - tidak ada gunanya mengkhawatirkan kecuali jika kinerja diidentifikasi sebagai masalah dan profiling menunjukkan bahwa membuat perubahan seperti itu akan memiliki dampak nyata.
Ben Cottrell

@ BenCottrell Saya akan menyebutnya optimasi "mikro" jika IsManagerVietnamVetproperti pergi untuk memeriksa dalam database;)
jitbit

3

Saya pikir opsi 3 (yaitu == true) adalah cara terbersih untuk menguji apakah bool?itu true, karena sangat jelas tentang apa yang dilakukannya.

Dalam kebanyakan kode x == truetidak masuk akal, karena itu sama dengan x, tetapi itu tidak berlaku di sini, jadi saya pikir == truetidak akan terlalu membingungkan.


1
Saya pikir ini pasti akan menjadi cara untuk pergi jika kita ingin menggunakan operator kondisional nol, karena itu juga aman linq. Pertanyaannya adalah apakah keterbacaannya bagus. Dari sudut pandang saya, pilihannya adalah antara opt 3 dan opt 4 dan saya akan memilih 4 lebih dari 3 untuk alasan keterbacaan, juga niat programmer tampaknya sedikit lebih jelas. Saya telah menandai jawaban Ben Cottrell sebagai benar karena saya lebih suka pendekatan itu. Jika saya dapat menandai dua jawaban, saya akan melakukannya.
Santhos

1
@Santhos - re "niat programmer sepertinya sedikit lebih jelas". IMHO, ini adalah kasus di mana pertama kali seorang programmer melihat a?.b == truemereka akan bingung, tetapi begitu mereka memahami apa yang dilakukannya, itu menjadi idiom yang sangat mudah dibaca, jauh lebih bagus daripada harus menguji != nulldi tengah ekspresi yang kompleks. Sama untuk a?.b ?? false, yang tampaknya telah memperoleh traksi sebagai solusi yang paling populer, karena cocok dengan apa yang akan Anda ketik jika Anda berurusan dengan jenis lain dari boolean [meskipun saya tidak seperti itu untuk boolean; bagi saya, itu tidak dibaca secara alami; Saya lebih suka == true].
ToolmakerSteve

3

Memperluas jawaban Ben Cottrell, pola "Null Object" dapat membantu Anda lebih jauh.

Alih-alih mengembalikan nulltim / manajer, ekstrak ITeamdan IManagerantarmuka dan kembalikan implementasi alternatif yang bermakna:

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

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

    public IManager Manager => new NoManager();
}

Maka tiba-tiba Anda bisa melakukannya team.ManagedByVietnamVetdengan aman.

Ini tentu saja bergantung pada penyedia hulu teamuntuk menjadi null-safe - tetapi itu dapat dipastikan dengan pengujian yang sesuai.


-4

Saya menulis kelas sederhana yang dapat Anda gunakan:

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

Jika dapat diandalkan untuk menggunakan tipe kustom, tentu saja. Anda dapat membandingkan MyBooldengan keduanya booldan Nullable<bool>.


1
Situs ini adalah untuk pertanyaan tentang desain perangkat lunak daripada bagaimana mendapatkan potongan kode tertentu untuk bekerja, jadi jika Anda percaya ini adalah solusi terbaik maka jawaban Anda harus fokus pada mengapa Anda percaya kelas seperti ini adalah solusi terbaik, bukan kode yang tepat diperlukan untuk mengimplementasikannya.
Ixrec
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.