ประเภท Nullable: วิธีที่ดีกว่าในการตรวจสอบค่าว่างหรือศูนย์ใน c #


85

ฉันกำลังทำงานในโครงการที่ฉันพบว่าฉันกำลังตรวจสอบสิ่งต่อไปนี้ในหลาย ๆ ที่:

if(item.Rate == 0 || item.Rate == null) { }

มากกว่าความอยากรู้อยากเห็นอะไรคือวิธีที่ดีที่สุดในการตรวจสอบทั้งสองกรณี

ฉันได้เพิ่มวิธีการช่วยเหลือซึ่งก็คือ:

public static bool nz(object obj)
{
    var parsedInt = 0;
    var parsed = int.TryParse(obj.ToString(), out parsedInt);
    return IsNull(obj) || (parsed && parsedInt == 0);
}

มีวิธีที่ดีกว่า?

คำตอบ:


162

ฉันชอบ if ((item.Rate ?? 0) == 0) { }

อัปเดต 1:

คุณยังสามารถกำหนดวิธีการขยายเช่น:

public static bool IsNullOrValue(this double? value, double valueToCheck)
{
    return (value??valueToCheck) == valueToCheck;
}

และใช้มันดังนี้:

if(item.IsNullOrValue(0)){} // แต่คุณไม่ได้รับอะไรมากมายจากมัน


3
ขอบคุณ - รวบรัดมาก! ฉันกังวลกับความสามารถในการอ่าน แต่สรุปได้ว่ามันจะอ่านได้อย่างสมบูรณ์แบบถ้าฉันเข้าใจจริง ๆ ?? ตัวดำเนินการ
nailitdown

2
คุณควรใช้วิธีการขยาย ในขณะที่สามารถอ่านได้ในขณะที่เขียนนักเก็ตโค้ดตัวน้อยนี้ต้องใช้ความคิดเพียงเล็กน้อยซึ่งหมายความว่าถ้าคุณพยายามอ่านโค้ดและใช้มันคุณจะเสียสมาธิจากปัญหาหลักของคุณ
กำหนดค่า

11
@nailitdown: ไม่จริงคุณแค่ต้องมีวิธีการขยายสำหรับ Nullable <T> สองเท่า? เป็นนามแฝงของ Nullable <double> ลายเซ็นของคุณจะมีลักษณะดังนี้: บูลคงที่สาธารณะ IsNullOrValue <T> (Nullable <T> นี้, t valueToCheck) โดยที่ T: struct
Joshua Shannon

3
@nailitdown: (ตอนที่ II) คำสั่ง return ของคุณจะมีลักษณะเหมือน return (value ?? default (T)) เท่ากับ (valueToCheck);
Joshua Shannon

2
หากคุณย่อให้สั้นลงเป็น "IsNullOr (0)" โดยปกติจะอ่านว่า "เป็นโมฆะหรือศูนย์"
ChrisFox

41

การใช้ยาชื่อสามัญ:

static bool IsNullOrDefault<T>(T value)
{
    return object.Equals(value, default(T));
}

//...
double d = 0;
IsNullOrDefault(d); // true
MyClass c = null;
IsNullOrDefault(c); // true

ถ้าTมันเป็นชนิดการอ้างอิง , valueจะได้รับเมื่อเทียบกับnull( default(T)) มิฉะนั้นถ้าTเป็นvalue typeสมมติว่าคู่default(t)เป็น 0d สำหรับบูลคือfalseสำหรับถ่าน'\0'และอื่น ๆ ...


1
วิธีการขยาย:public static bool IsNullOrValue<T>(this T? value, T valueToCheck) where T : struct { return (value ?? default(T)).Equals(valueToCheck); }
Behzad Ebrahimi

39

แม้ว่าฉันจะชอบคำตอบที่ยอมรับ แต่ฉันคิดว่าเพื่อความสมบูรณ์ควรกล่าวถึงตัวเลือกนี้ด้วย:

if (item.Rate.GetValueOrDefault() == 0) { }

วิธีแก้ปัญหานี้


¹สิ่งนี้ไม่ควรส่งผลต่อการตัดสินใจของคุณเนื่องจากการเพิ่มประสิทธิภาพระดับไมโครประเภทนี้ไม่น่าจะสร้างความแตกต่างได้


19

นี่เป็นเพียงการขยายคำตอบที่ยอมรับของ Freddy Rios โดยใช้ Generics เท่านั้น

public static bool IsNullOrDefault<T>(this Nullable<T> value) where T : struct
{
    return default(T).Equals( value.GetValueOrDefault() );
}

public static bool IsValue<T>(this Nullable<T> value, T valueToCheck) where T : struct
{
    return valueToCheck.Equals((value ?? valueToCheck));
}

โปรดทราบว่าเราไม่จำเป็นต้องตรวจสอบค่าเริ่มต้น (T) สำหรับ null เนื่องจากเรากำลังจัดการกับประเภทค่าหรือโครงสร้าง! นอกจากนี้ยังหมายความว่าเราสามารถถือว่า T valueToCheck ได้อย่างปลอดภัยจะไม่เป็นโมฆะ จำไว้ตรงนี้ว่า T? ชวเลขเป็น Nullable <T> ดังนั้นโดยการเพิ่มส่วนขยายเป็น Nullable <T> เราจะได้เมธอดใน int?, double?, bool? เป็นต้น

ตัวอย่าง:

double? x = null;
x.IsNullOrDefault(); //true

int? y = 3;
y.IsNullOrDefault(); //false

bool? z = false;
z.IsNullOrDefault(); //true

2

ฉันเห็นด้วยกับการใช้ ?? ตัวดำเนินการ

หากคุณกำลังจัดการกับสตริงให้ใช้ if (String.IsNullOrEmpty (myStr))


2

มีวิธีที่ดีกว่า?

ถ้าคุณกำลังมองหาวิธีที่ดีกว่านี้จริงๆคุณอาจเพิ่มนามธรรมอีกชั้นที่ด้านบนของอัตรา นี่คือสิ่งที่ฉันเพิ่งสร้างขึ้นโดยใช้ Nullable Design Pattern

ใช้ระบบ;
ใช้ System.Collections.Generic;

เนมสเปซ NullObjectPatternTest
{
    โปรแกรมชั้นเรียนสาธารณะ
    {
        โมฆะคงที่สาธารณะ Main (สตริง [] args)
        {
            var items = รายการใหม่
                            {
                                รายการใหม่ (RateFactory.Create (20))
                                รายการใหม่ (RateFactory.Create (null))
                            };

            PrintPricesForItems (รายการ);
        }

        โมฆะคงที่ส่วนตัว PrintPricesForItems (IEnumerable items)
        {
            foreach (รายการ var ในรายการ)
                Console.WriteLine ("ราคาสินค้า: {0: C}", item.GetPrice ());
        }
    }

    คลาสนามธรรมสาธารณะ ItemBase
    {
        สาธารณะนามธรรม Rate Rate {get; }
        int สาธารณะ GetPrice ()
        {
            // ไม่จำเป็นต้องตรวจสอบว่า Rate == 0 หรือ Rate == null
            คืน 1 * Rate.Value;
        }
    }

    คลาสสาธารณะ Item: ItemBase
    {
        อัตราแบบอ่านอย่างเดียวส่วนตัว _Rate;
        public override Rate Rate {รับ {return _Rate; }}
        Public Item (เรทเรท) {_Rate = rate; }
    }

    ระดับปิดผนึกสาธารณะ RateFactory
    {
        สร้างอัตราคงที่สาธารณะ (int? rateValue)
        {
            ถ้า (! rateValue || rateValue == 0) 
                ส่งคืน NullRate ใหม่ ();
            ส่งคืนอัตราใหม่ (rateValue);
        }
    }

    อัตราชั้นสาธารณะ
    {
        ค่า int สาธารณะ {get; ชุด; }
        บูลเสมือนสาธารณะ HasValue {รับ {return (Value> 0); }}
        อัตราสาธารณะ (ค่า int) {Value = value; }
    }

    NullRate ระดับสาธารณะ: อัตรา
    {
        Public override bool HasValue {get {return false; }}
        NullRate สาธารณะ (): ฐาน (0) {}
    }
}

1
ฉันคิดว่าคุณกำลังขวาที่ขจัดค่า null ในบางขั้นตอนก่อนหน้านี้จะได้รับวิธีที่จะไป
nailitdown

ไม่ตรง มีแนวคิดที่เรียกว่า Refactoring คุณสามารถ refactor โค้ดของคุณไปสู่รูปแบบที่ดีขึ้นหรือโครงสร้างที่ดีขึ้น คุณสามารถกำจัดค่าที่เป็นโมฆะได้ในระยะต่อมา
dance2die

2

ตัวอย่างโค้ดของคุณจะล้มเหลว ถ้า obj เป็นโมฆะ obj ToString () จะทำให้เกิดข้อยกเว้นการอ้างอิงที่เป็นโมฆะ ฉันจะตัดขั้นตอนให้สั้นลงและตรวจสอบ null obj ที่จุดเริ่มต้นของฟังก์ชันตัวช่วยของคุณ สำหรับคำถามที่แท้จริงของคุณประเภทที่คุณกำลังตรวจสอบค่าว่างหรือศูนย์คืออะไร? On String มีฟังก์ชัน IsNullOrEmpty ที่ยอดเยี่ยมสำหรับฉันแล้วนี่จะเป็นการใช้วิธีการขยายที่ยอดเยี่ยมในการใช้เมธอด IsNullOrZero ใน int? ชนิด.

แก้ไข: จำไว้ว่า "?" เป็นเพียงน้ำตาลคอมไพเลอร์สำหรับประเภท INullable ดังนั้นคุณอาจใช้ INullable เป็น parm จากนั้น jsut เปรียบเทียบกับ null (parm == null) และถ้าไม่เป็น null ให้เปรียบเทียบกับศูนย์


ไชโยสำหรับการจับจุดบกพร่องนั้น - ในโครงการนี้ฉันต้องตรวจสอบกับ int?, double?,
decimal

จำไว้ว่า "?" เป็นเพียงน้ำตาลคอมไพเลอร์สำหรับประเภท INullable <T> ดังนั้นคุณอาจใช้ INullable <T> เป็น parm จากนั้น jsut เปรียบเทียบกับ null (parm == null) และถ้าไม่ใช่ null ให้เปรียบเทียบกับศูนย์
Walden Leverich

0
public static bool nz(object obj)
{
    return obj == null || obj.Equals(Activator.CreateInstance(obj.GetType()));
}

1
การสะท้อนกลับช้าระวังด้วยการแก้ปัญหาเช่นนี้ ฉันไม่คัดค้านการไตร่ตรอง แต่ฉันไม่คาดหวังว่ามันจะเป็นฟังก์ชัน "ธรรมดา ๆ " เช่นนี้และมันจะทำให้ฉันไม่ทันระวัง
Walden Leverich

นี่ตรวจสอบศูนย์จริงหรือ
nailitdown

จริงๆแล้วไม่ ฉันเชื่อว่า Activator.CreateInstance (obj.GetType ()) จะคืนค่า null สำหรับประเภทที่เป็นโมฆะ
กำหนดค่า

@configurator: เมื่อชนิดที่เป็นโมฆะถูกใส่กล่องพวกมันจะถูกจัดให้เป็นโครงสร้างพื้นฐานกล่าวคือดังนั้นสิ่งนี้จะไม่สร้างค่า null ที่เป็นค่าว่างได้ แต่เป็นโครงสร้างที่ไม่มีค่าเริ่มต้นที่ไม่เป็นค่าว่าง
Eamon Nerbonne

0
class Item{  
 bool IsNullOrZero{ get{return ((this.Rate ?? 0) == 0);}}
}

โซลูชันของคุณใช้ได้กับคุณสมบัติเดียวฉันควรระบุว่าฉันมีคุณสมบัติหลายรายการของรายการเดียวกันเพื่อตรวจสอบค่า null / zero
nailitdown

0

อย่าลืมสำหรับสตริงคุณสามารถใช้:

String.IsNullOrEmpty(str)

แทน:

str==null || str==""

1
คำถามเปรียบเทียบกับ "0" ไม่ใช่สตริงว่างเปล่า
dance2die

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