วิธีที่ดีที่สุดในการใช้ตัวดำเนินการเงื่อนไขแบบ null ในนิพจน์บูลีน


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 มันก็ ment จะเป็นเหมือนสิทธินี้ตั้งแต่เริ่มต้น;)
Santhos

1
มองไปที่รหัสอย่างระมัดระวังมากขึ้นส่วนหนึ่งเงื่อนไข null ที่ควรจะเป็นteam.Manager?.IsVietnamVetคือไม่มี null หลังจากเงื่อนไขตั้งแต่อยู่แล้วไม่สามารถteam null
svick

2
"คุณจะเขียนถ้า (เงื่อนไข == จริง) โดยไม่ลังเล?" สำหรับบูลีนปกติไม่เพราะมันฟุ่มเฟือย อย่างไรก็ตามสำหรับบูลีนแบบ nullableสิ่งนี้มีความเกี่ยวข้อง nullableBool == trueเป็นพื้นทดสอบnullableBool != false && nullableBool != null(และมันเป็นส่วนหนึ่งที่สองนี้ที่ทำให้มันมีประโยชน์และดังนั้นจึงไม่ฟุ่มเฟือย)
Flater

2
ฉันเริ่มใช้nullableBool == trueถ้าฉันไม่ได้สร้างเสื้อคลุม สามารถอ่านได้เพราะปกติคุณจะไม่เขียน== trueเมื่อใช้บูลีนปกติตามที่ @Flater กล่าวถึงดังนั้นจึงแนะนำว่าตัวแปรนั้นเป็นโมฆะ ตามปกติแล้วจะปรับปรุงการอ่าน LINQ ได้เนื่องจากคุณไม่ได้ใช้หลายเงื่อนไขเป็น @Fabio กล่าวถึง
Santhos

คำตอบ:


4

ในกรณีพิเศษนี้คุณควรปฏิบัติตามกฎหมายของ Demeterเช่น

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 # ไวยากรณ์เพื่อบันทึกไม่กี่บรรทัด: สาธารณะบูล IsManagerVietnamVet => (team.Category == "") && (team.Manager?. IsVietnamVet ?? false);
เกรแฮม

เพียงแค่เก็บไว้ในใจมานานแล้วว่าจะดำเนินการตามลำดับและเป็นคนต่อไปจะไม่ทำงานแม้ว่าก่อนหน้านี้หนึ่งคือa && b && c &&... falseดังนั้นผู้คนมักจะเริ่มต้นเร็วที่สุด โปรดทราบว่าเมื่อมีการย้ายสิ่งของไปไว้ใน bool-var
jitbit ที่

@jitbit มันเพิ่มเติมเกี่ยวกับการบำรุงรักษา การปรับให้เหมาะสมขนาดเล็กเช่นที่คุณพูดถึงนั้นไม่จำเป็นโดยทั่วไป - มันไม่คุ้มค่าที่จะต้องกังวลนอกจากว่าประสิทธิภาพจะถูกระบุว่าเป็นปัญหาและการทำโปรไฟล์แสดงให้เห็นว่าการเปลี่ยนแปลงเช่นนั้นจะส่งผลกระทบใด ๆ
Ben Cottrell

@ BenCottrell ฉันจะเรียกมันว่าการเพิ่มประสิทธิภาพ "ไมโคร" ถ้าIsManagerVietnamVetสถานที่ให้บริการจะไปตรวจสอบในฐานข้อมูล;)
35711

3

ฉันคิดว่าตัวเลือก 3 (เช่น== true) เป็นวิธีที่สะอาดที่สุดในการทดสอบว่าbool?เป็นtrueเพราะมันชัดเจนมากเกี่ยวกับสิ่งที่ทำ

ในโค้ดส่วนใหญ่x == trueไม่สมเหตุสมผลเนื่องจากมันเป็นแบบเดียวกันxแต่ไม่ได้ใช้ที่นี่ดังนั้นฉันคิดว่า== trueจะไม่สับสนมาก


1
ฉันคิดว่านี่จะเป็นวิธีที่แน่นอนหากเราต้องการใช้ตัวดำเนินการแบบไม่มีเงื่อนไขเพราะมันปลอดภัยด้วย linq คำถามคือการอ่านดีหรือไม่ จากมุมมองของฉันตัวเลือกอยู่ระหว่าง opt 3 และ opt 4 และฉันจะเลือก 4 มากกว่า 3 สำหรับเหตุผลในการอ่านและความตั้งใจของโปรแกรมเมอร์ดูเหมือนชัดเจนขึ้น ฉันได้ทำเครื่องหมายคำตอบของ Ben Cottrell ว่าถูกต้องเพราะฉันชอบวิธีนั้นมากกว่านี้ ถ้าฉันสามารถทำเครื่องหมายสองคำตอบฉันจะ
Santhos

1
@Santos - อีกครั้ง "ความตั้งใจของโปรแกรมเมอร์ดูเหมือนจะชัดเจนขึ้นเล็กน้อย" IMHO นี่เป็นกรณีที่ครั้งแรกที่โปรแกรมเมอร์เห็นว่าa?.b == trueพวกเขาจะสับสน แต่เมื่อพวกเขาเข้าใจว่ามันทำอะไรมันจะกลายเป็นสำนวนที่อ่านง่ายมากซึ่งดีกว่าการทดสอบ!= nullกลางนิพจน์ที่ซับซ้อน เหมือนกันa?.b ?? falseซึ่งดูเหมือนว่าจะได้รับการฉุดลากเป็นทางออกที่นิยมมากที่สุดเพราะมันตรงกับสิ่งที่คุณจะพิมพ์ถ้าคุณกำลังจัดการกับประเภทอื่นที่ไม่ใช่บูลีน [แม้ว่าฉันจะไม่ชอบมันสำหรับบูลีน; สำหรับฉันมันไม่ได้อ่านตามธรรมชาติ; ฉันชอบ== true]
ToolmakerSteve

3

ขยายคำตอบของ Ben Cottrell รูปแบบ "Null Object" สามารถช่วยคุณได้มากขึ้น

แทนที่จะส่งคืน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กับทั้งและboolNullable<bool>


1
ไซต์นี้มีคำถามเกี่ยวกับการออกแบบซอฟต์แวร์แทนที่จะทำอย่างไรให้โค้ดบางชิ้นทำงานได้ดังนั้นหากคุณเชื่อว่านี่เป็นทางออกที่ดีที่สุดคำตอบของคุณควรมุ่งเน้นไปที่สาเหตุที่คุณเชื่อว่าคลาสเช่นนี้เป็นทางออกที่ดีที่สุดไม่ใช่ รหัสที่แน่นอนที่จำเป็นในการใช้งาน
Ixrec
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.