มีการเรียกเมธอดด้วยค่า Null หรือให้ข้อยกเว้นการอ้างอิง Null หรือไม่?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
หากเป็นกรณีนี้ฉันไม่จำเป็นต้องตรวจสอบพารามิเตอร์ 'this' ของฉันอีกเลยใช่ไหม
มีการเรียกเมธอดด้วยค่า Null หรือให้ข้อยกเว้นการอ้างอิง Null หรือไม่?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
หากเป็นกรณีนี้ฉันไม่จำเป็นต้องตรวจสอบพารามิเตอร์ 'this' ของฉันอีกเลยใช่ไหม
คำตอบ:
ที่จะทำงานได้ดี (ไม่มีข้อยกเว้น) วิธีการขยายไม่ใช้การโทรเสมือน (เช่นใช้คำสั่ง "call" il ไม่ใช่ "callvirt") ดังนั้นจึงไม่มีการตรวจสอบค่าว่างนอกจากคุณจะเขียนด้วยตัวคุณเองในวิธีการขยาย นี่เป็นประโยชน์จริง ๆ ในบางกรณี:
public static bool IsNullOrEmpty(this string value)
{
return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
where T : class
{
if(obj == null) throw new ArgumentNullException(parameterName);
}
ฯลฯ
โดยพื้นฐานแล้วการโทรไปยังสายคงที่นั้นแท้จริงมาก - เช่น
string s = ...
if(s.IsNullOrEmpty()) {...}
กลายเป็น:
string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}
เห็นได้ชัดว่าไม่มีการตรวจสอบเป็นโมฆะ
นอกเหนือจากคำตอบที่ถูกต้องจาก Marc Gravell
คุณสามารถรับคำเตือนจากคอมไพเลอร์หากเห็นได้ชัดว่าอาร์กิวเมนต์นี้เป็นโมฆะ:
default(string).MyExtension();
ทำงานได้ดีที่รันไทม์ "Expression will always cause a System.NullReferenceException, because the default value of string is null"
แต่ผลิตเตือน
ในขณะที่คุณค้นพบแล้วเนื่องจากวิธีการขยายเป็นวิธีการคงที่สดุดีเพียงแค่พวกเขาจะถูกเรียกว่ามีnull
การอ้างอิงผ่านในโดยไม่ต้องNullReferenceException
ถูกโยน แต่เนื่องจากพวกเขาดูเหมือนวิธีการแบบอินสแตนซ์สำหรับผู้โทรพวกเขาจึงควรประพฤติตนเช่นนั้น จากนั้นคุณควรตรวจสอบthis
พารามิเตอร์และทิ้งข้อผิดพลาดเป็นส่วนnull
ใหญ่ มันก็โอเคที่จะไม่ทำเช่นนี้หากวิธีการดูแลnull
ค่าอย่างชัดเจนและชื่อของมันบ่งบอกว่าถูกต้องเช่นในตัวอย่างด้านล่าง:
public static class StringNullExtensions {
public static bool IsNullOrEmpty(this string s) {
return string.IsNullOrEmpty(s);
}
public static bool IsNullOrBlank(this string s) {
return s == null || s.Trim().Length == 0;
}
}
ฉันเคยเขียนบล็อกโพสต์เกี่ยวกับเรื่องนี้เมื่อไม่นานมานี้
null จะถูกส่งผ่านไปยังวิธีการขยาย
หากวิธีการพยายามที่จะเข้าถึงวัตถุโดยไม่ตรวจสอบมันเป็นโมฆะใช่แล้วมันจะโยนข้อยกเว้น
ผู้ชายที่นี่เขียนวิธีการขยาย "IsNull" และ "IsNotNull" ที่ตรวจสอบว่าการอ้างอิงนั้นผ่านไปแล้วหรือไม่ โดยส่วนตัวฉันคิดว่านี่เป็นความผิดปกติและไม่ควรเห็นแสงของวัน แต่มันใช้ได้อย่างสมบูรณ์แบบ c #
ดังที่คนอื่น ๆ ชี้ให้เห็นการเรียกวิธีการขยายในการอ้างอิงแบบ null ทำให้อาร์กิวเมนต์นี้เป็นโมฆะและไม่มีอะไรพิเศษเกิดขึ้น สิ่งนี้ทำให้เกิดความคิดที่จะใช้วิธีการขยายในการเขียนคำสั่งป้องกัน
คุณสามารถอ่านบทความนี้สำหรับตัวอย่าง: วิธีการลดความซับซ้อนของวัฏจักร: ข้อที่รุ่นสั้นคือ:
public static class StringExtensions
{
public static void AssertNonEmpty(this string value, string paramName)
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Value must be a non-empty string.", paramName);
}
}
นี่คือวิธีการขยายคลาสสตริงซึ่งสามารถเรียกใช้ในการอ้างอิงเป็นโมฆะ:
((string)null).AssertNonEmpty("null");
การโทรทำงานได้ดีเพียงเพราะรันไทม์จะเรียกวิธีการส่วนขยายในการอ้างอิงเป็นค่าว่าง จากนั้นคุณสามารถใช้วิธีการขยายนี้เพื่อใช้คำสั่งป้องกันโดยไม่มีไวยากรณ์ยุ่ง:
public IRegisteredUser RegisterUser(string userName, string referrerName)
{
userName.AssertNonEmpty("userName");
referrerName.AssertNonEmpty("referrerName");
...
}
วิธีการขยายคงที่ดังนั้นถ้าคุณไม่ทำอะไรกับ MyObject นี้มันไม่น่าจะมีปัญหาการทดสอบอย่างรวดเร็วควรตรวจสอบมัน :)
มีกฎทองเล็กน้อยเมื่อคุณต้องการให้คุณอ่านและเป็นแนวตั้ง
ในกรณีของคุณ - DesignByContract ใช้งานไม่ได้ ... คุณจะต้องใช้ตรรกะบางอย่างในอินสแตนซ์ที่เป็นโมฆะ
Cannot perform runtime binding on a null reference
นี้