ทำเครื่องหมายพารามิเตอร์ว่าไม่เป็นโมฆะใน C # /. NET?


99

มีแอตทริบิวต์หรือสัญญาข้อมูลอย่างง่ายที่ฉันสามารถกำหนดให้กับพารามิเตอร์ฟังก์ชันที่ป้องกันไม่ให้nullส่งผ่านใน C # /. NET ได้หรือไม่ นึกคิดนี้ยังจะตรวจสอบที่รวบรวมเวลาเพื่อให้แน่ใจว่าที่แท้จริงได้ทุกที่ไม่ได้ถูกใช้สำหรับมันและที่ใช้เวลาโยนnullArgumentNullException

ตอนนี้ฉันเขียนบางอย่างเช่น ...

if (null == arg)
  throw new ArgumentNullException("arg");

... สำหรับทุกการโต้แย้งที่ฉันคาดหวังว่าจะไม่เกิดnullขึ้น

ในบันทึกเดียวกันมีสิ่งที่ตรงกันข้ามกับNullable<>สิ่งต่อไปนี้หรือไม่:

NonNullable<string> s = null; // throw some kind of exception

7
ด้วย C # เวอร์ชันล่าสุดการใช้ "nameof ()" จะทำงานได้ดีขึ้น: โยน ArgumentNullException ใหม่ (nameof (arg)); ด้วยวิธีนี้หากคุณเปลี่ยนชื่อใหม่หากกระเพื่อมเป็นคำสั่งโยนของคุณ
Flydog57

ตอนนี้ C # 8 รองรับประเภทการอ้างอิงที่เป็นโมฆะซึ่งเป็นตัวเลือกคอมไพเลอร์ที่คุณสามารถเปิดใช้งานในโปรเจ็กต์ของคุณได้ docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

คำตอบ:


69

ไม่มีสิ่งใดในเวลาคอมไพล์น่าเสียดาย

ฉันมีวิธีแก้ปัญหาแฮ็กนิดหน่อยที่ฉันโพสต์ในบล็อกเมื่อเร็ว ๆ นี้ซึ่งใช้โครงสร้างและการแปลงใหม่

ใน. NET 4.0 พร้อมกับCode Contractsชีวิตจะดีขึ้นมาก ยังคงเป็นเรื่องดีที่จะมีไวยากรณ์ภาษาจริงและการสนับสนุนเกี่ยวกับ non-nullability แต่สัญญารหัสจะช่วยได้มาก

ฉันยังมีวิธีการขยายในMiscUtil ที่เรียกว่า ThrowIfNull ซึ่งทำให้ง่ายขึ้นเล็กน้อย

ประเด็นสุดท้าย - เหตุผลใดในการใช้ " if (null == arg)" แทน " if (arg == null)" ฉันพบว่าข้อหลังนี้อ่านง่ายขึ้นและปัญหาที่เคยแก้ใน C ใช้ไม่ได้กับ C #


2
> ไม่มีสิ่งใดในเวลาคอมไพล์ แต่น่าเสียดาย > ... > ยังคงเป็นเรื่องดีที่จะมีไวยากรณ์ภาษาจริงและการสนับสนุนเกี่ยวกับความไม่เป็นโมฆะฉันเห็นด้วย ฉันต้องการเห็นข้อผิดพลาดเวลาคอมไพล์ที่เพิ่มขึ้นด้วย
AndrewJacksonZA

2
@ จอนไม่ "if (arg = null)" ทำงานหากมีการส่งโดยนัยไปยัง bool ที่กำหนดไว้? ฉันยอมรับว่ามันอาจดูวิปริต แต่มันรวบรวม ...
Thomas S. Trias

11
@ ThomasS.Trias: ใช่ในกรณีขอบที่คลุมเครืออย่างไม่น่าเชื่อเมื่อรวมกับการพิมพ์ผิดรวมกับการขาดการทดสอบเกี่ยวกับรหัสนั้นคุณจะพบปัญหา เมื่อถึงจุดนั้นฉันคิดว่าคุณมีปัญหาที่ใหญ่กว่า :)
Jon Skeet

4
@ จอน: รับ. ฉันเดาว่าฉันจะยอมแพ้เงื่อนไขโยดาที่รัก :-)
Thomas S. Trias

1
@SebastianMach: ฉันไม่เชื่อว่าสาเหตุif (null == x)เกิดจากความแตกต่างของภาษาตามธรรมชาติ ฉันเชื่อว่ามันเกี่ยวกับรูปแบบที่ล้าสมัยซึ่งเพิ่งเผยแพร่ผ่านตัวอย่างเป็นต้น
Jon Skeet

23

ฉันรู้ว่าฉันมาสายอย่างไม่น่าเชื่อสำหรับคำถามนี้ แต่ฉันรู้สึกว่าคำตอบจะมีความเกี่ยวข้องเมื่อการทำซ้ำครั้งใหญ่ล่าสุดของ C # ใกล้จะออกแล้ว ใน C # 8.0 จะเกิดการเปลี่ยนแปลงที่สำคัญ C # จะถือว่าทุกประเภทไม่เป็นโมฆะ

ตาม Mads Torgersen:

ปัญหาคือการอ้างอิงว่างมีประโยชน์มาก ใน C # เป็นค่าเริ่มต้นของการอ้างอิงทุกประเภท ค่าเริ่มต้นจะเป็นอย่างไร ตัวแปรจะมีค่าอะไรอีกจนกว่าคุณจะสามารถตัดสินใจได้ว่าจะกำหนดค่าอะไรอีก ค่าอื่นใดที่เราสามารถปูอาร์เรย์การอ้างอิงที่จัดสรรใหม่ได้จนกว่าคุณจะกรอกข้อมูลลงไป

นอกจากนี้บางครั้งค่าว่างก็เป็นค่าที่สมเหตุสมผลในตัวของมันเอง บางครั้งคุณต้องการแสดงความจริงที่ว่าเขตข้อมูลไม่มีค่า เป็นเรื่องปกติที่จะส่ง "nothing" สำหรับพารามิเตอร์ แม้ว่าบางครั้งจะเน้น และในที่นี้เป็นอีกส่วนหนึ่งของปัญหา: ภาษาเช่น C # อย่าให้คุณระบุว่าโมฆะตรงนี้เป็นความคิดที่ดีหรือไม่

ดังนั้นความละเอียดที่ Mads ระบุไว้คือ:

  1. เราเชื่อว่าเป็นเรื่องธรรมดาที่จะต้องการให้ข้อมูลอ้างอิงไม่เป็นค่าว่าง ประเภทการอ้างอิงที่เป็นโมฆะจะเป็นประเภทที่หายากกว่า (แม้ว่าเราจะไม่มีข้อมูลที่ดีพอที่จะบอกเราได้ว่ามีมากแค่ไหนก็ตาม) ดังนั้นจึงเป็นประเภทที่ควรต้องมีคำอธิบายประกอบใหม่

  2. ภาษามีแนวคิดเกี่ยวกับ - และไวยากรณ์สำหรับ - ประเภทค่าที่เป็นโมฆะอยู่แล้ว การเปรียบเทียบระหว่างทั้งสองจะทำให้การเพิ่มภาษาในเชิงแนวคิดง่ายขึ้นและง่ายขึ้นในทางภาษา

  3. ดูเหมือนถูกแล้วที่คุณไม่ควรสร้างภาระให้กับตัวเองหรือผู้บริโภคด้วยค่า null ที่ยุ่งยากเว้นแต่คุณจะตัดสินใจอย่างจริงจังว่าต้องการ Nulls ไม่ใช่การไม่มีอยู่ควรเป็นสิ่งที่คุณต้องเลือกใช้อย่างชัดเจน

ตัวอย่างคุณสมบัติที่ต้องการ:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

ตัวอย่างมีให้สำหรับ Visual Studio 2017, 15.5.4+ พรีวิว


1
ไม่มีใครรู้ว่าสิ่งนี้เคยเป็นส่วนหนึ่งของ C # 8.0 หรือไม่?
TroySteven

@TroySteven ใช่แล้วคุณต้องเลือกใช้ผ่านการตั้งค่าใน Visual Studio
Greg

@TroySteven นี่คือเอกสารประกอบ docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Greg

ดีดังนั้นดูเหมือนว่าคุณต้องเปิดใช้งานก่อนที่จะเริ่มทำงานซึ่งเป็นสิ่งที่ดีสำหรับความเข้ากันได้แบบย้อนกลับ
TroySteven

15

ฉันรู้ว่านี่เป็นคำถามเก่ามาก แต่คำถามนี้หายไปที่นี่:

ถ้าคุณใช้ ReSharper / ไรเดอร์คุณอาจใช้ข้อเขียนกรอบ

แก้ไข : ฉันได้รับ -1 แบบสุ่มสำหรับคำตอบนี้ ไม่เป็นไร. เพียงแค่ทราบว่ายังใช้ได้แม้ว่าจะไม่ใช่แนวทางที่แนะนำสำหรับโครงการ C # 8.0 + อีกต่อไป (หากต้องการทำความเข้าใจว่าเหตุใดโปรดดูคำตอบของ Greg )


1
ที่น่าสนใจคือโดยค่าเริ่มต้นแอปพลิเคชันที่คอมไพล์ของคุณจะไม่มีการอ้างอิงถึง JetBrains.Annotations.dll ดังนั้นคุณไม่จำเป็นต้องแจกจ่ายกับแอปพลิเคชัน: วิธีใช้คำอธิบายประกอบของ JetBrains เพื่อปรับปรุงการตรวจสอบ ReSharper
Lu55

9

ตรวจสอบตัวตรวจสอบความถูกต้องในไลบรารีขององค์กร คุณสามารถทำสิ่งต่างๆเช่น:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

จากนั้นในรหัสของคุณเมื่อคุณต้องการตรวจสอบ:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);

0

ไม่ใช่คนที่สวยที่สุด แต่:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

คุณสามารถสร้างสรรค์ได้มากขึ้นในเมธอด containsNullParameters ด้วย:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

แน่นอนคุณสามารถใช้ตัวสกัดกั้นหรือการสะท้อนกลับได้ แต่สิ่งเหล่านี้ง่ายต่อการติดตาม / ใช้งานโดยมีค่าใช้จ่ายเพียงเล็กน้อย


3
วิธีปฏิบัติที่ไม่ดีอย่างยิ่งในการใช้คำค้นหาดังกล่าว: return (from o in methodParams where o == null).Count() > 0; ใช้: return methodParams.Any(o=>o==null);จะเร็วกว่ามากในคอลเลกชันขนาดใหญ่
Yavanosta

ทำไมถึงผ่านข้อยกเว้น? ทำไมไม่แค่โยนมัน?
Martin Capodici

-4

ตกลงคำตอบนี้ช้าไปหน่อย แต่นี่คือวิธีแก้ปัญหา:

public static string Default(this string x)
{
    return x ?? "";
}

ใช้วิธีการ exension นี้จากนั้นคุณสามารถถือว่าสตริงว่างและว่างเป็นสิ่งเดียวกัน

เช่น

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

ไม่เหมาะอย่างยิ่งฉันรู้ว่าคุณต้องจำไว้ว่าให้เรียกค่าเริ่มต้นทุกที่ แต่เป็นทางออกหนึ่ง


5
วิธีนี้ดีกว่า / ง่ายกว่าการตรวจสอบ (x == null) อย่างไร หรือฟังก์ชัน String.IsNotNullOrEmpty
Batavia

ไม่ได้ดีไปstring.IsNotNullOrEmptyกว่าน้ำตาลที่มีสิ่งที่คุณสนใจอยู่ทางด้านซ้าย สามารถป้อนลงในฟังก์ชันอื่น ๆ (ความยาวการเรียงต่อกัน) ฯลฯ ได้ดีขึ้นเล็กน้อย
Martin Capodici

@MartinCapodici นอกจากนี้สิ่งนี้จะสร้างภาพลวงตาว่าคุณปลอดภัยที่จะเรียกวิธีการอินสแตนซ์ ( Default()) บนnull ( model.Day) ฉันรู้ว่าวิธีการขยายไม่ได้ตรวจสอบกับโมฆะ แต่ตาของฉันไม่รู้และพวกเขาจินตนาการ?ถึงรหัสแล้ว: model?.Day?.Default():)
Alb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.