สร้าง Null Objects ด้วย Null-Coalescing Operator


12

พิจารณาสถานการณ์ทั่วไปดังต่อไปนี้:

if(myObject == null) {
    myObject = new myClass();
}

ฉันสงสัยว่าความคิดของการแทนที่ต่อไปนี้โดยใช้ตัวดำเนินการ null รวมกัน:

myObject = myObject ?? new myClass();

ฉันไม่แน่ใจว่าควรใช้แบบฟอร์มที่สองหรือไม่ ดูเหมือนว่าจะเป็นชวเลขที่ดี แต่การmyObject = myObjectสร้างที่จุดเริ่มต้นดูเหมือนจะเป็นกลิ่นโค้ดเล็กน้อย

นี่เป็นสิ่งที่สมเหตุสมผลที่ต้องทำหรือมีชวเลขที่ดีกว่าที่ฉันทำหายไปหรือไม่? หรืออาจจะ "มันเป็นสามบรรทัดเอาเลย!"?

แก้ไข: ดังที่ได้กล่าวไว้บางทีการเรียกสิ่งนี้ว่าสถานการณ์โดยทั่วไปนั้นเป็นเรื่องที่เกินความจริง ฉันมักจะพบว่าฉันพบสถานการณ์นี้เมื่อฉันดึงข้อมูลเอนทิตี้จากฐานข้อมูลที่มีคุณสมบัติประเภทการอ้างอิงลูกที่อาจหรืออาจจะไม่ได้บรรจุ:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
มือใหม่เชื่อว่าสิ่งนี้จะ "แฮ็ก" และมีประสบการณ์มากขึ้นเพียงเรียกว่า "สำนวน"
Doc Brown

หากวัตถุดูเหมือนว่าวัตถุค่า ("มีความหมายตามตัวอักษร" ในการพูดสำนวน) MyClassควรให้public static readonlyสมาชิกของEmptyค่าประเภทของมัน ดูString.Emptyใน MSDN
ร. ว.


5
ไม่มี??=โอเปอเรเตอร์หรือ
aviv

1
@aviv ฉันหวังว่าจะมี!
Loren Pechtel

คำตอบ:


16

ฉันใช้ตัวดำเนินการรวมศูนย์ว่างตลอดเวลา ฉันชอบความกระชับของมัน

ฉันพบว่าตัวดำเนินการนี้คล้ายคลึงกันกับตัวดำเนินการแบบไตรภาค (A? B: C) ใช้เวลาฝึกเล็กน้อยก่อนที่การอ่านจะเป็นลักษณะที่สอง แต่เมื่อคุณคุ้นเคยกับมันแล้วฉันรู้สึกว่าการอ่านง่ายขึ้นดีขึ้นกว่าเวอร์ชั่นที่ใช้เวลานาน

นอกจากนี้สถานการณ์ที่คุณอธิบายเป็นเพียงสถานการณ์เดียวที่ผู้ประกอบการมีประโยชน์ นอกจากนี้ยังสะดวกในการแทนที่การสร้างเช่นนี้:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

หรือ

return value != null ? value : otherValue;

กับ

return value ?? otherValue;

เห็นด้วยอย่างแน่นอนโดยทั่วไปเกี่ยวกับการใช้ตัวดำเนินการนี้ เมื่อฉันเห็นการอ้างอิงถึงการใช้มัน myObject = myObject2 ?? myObject3แต่ก็มักจะเป็นสิ่งที่ชอบ สิ่งที่ฉันสงสัยโดยเฉพาะคือการใช้โอเปอเรเตอร์เพื่อตั้งค่าวัตถุให้กับตัวเองยกเว้นว่ามันจะเป็นโมฆะ
grin0048

2
ฉันคิดว่าการใช้เหตุผลแบบเดียวกันไม่ว่าในกรณีใด มันเป็นวิธีที่กระชับกว่าในการแสดงตรรกะเดียวกัน
17 ของ 26

ฉันจำการดู IL สำหรับสามรูปแบบเหล่านั้นเพียงครั้งเดียว ตัวแรกและตัวที่สองเหมือนกัน แต่ตัวที่สามได้รับการปรับให้เหมาะกับแฟชั่นมากที่สุดเท่าที่จะทำได้nullเป็นการเปรียบเทียบ
Jesse C. Slicer

2

สิ่งที่ฉันสงสัยโดยเฉพาะคือการใช้โอเปอเรเตอร์เพื่อตั้งค่าวัตถุให้กับตัวเองยกเว้นว่ามันจะเป็นโมฆะ

ตัวดำเนินการ?? operatorนี้เรียกว่าตัวดำเนินการ null รวมและใช้เพื่อกำหนดค่าเริ่มต้นสำหรับชนิดค่าที่เป็นโมฆะหรือประเภทการอ้างอิง มันจะส่งกลับตัวถูกดำเนินการทางซ้ายถ้าตัวถูกดำเนินการไม่เป็นโมฆะ มิฉะนั้นจะส่งคืนตัวถูกดำเนินการที่ถูกต้อง

myObject = myObject ?? new myObject(); - instantiate default value of object

รายละเอียดเพิ่มเติมและตัวอย่างโค้ดเกี่ยวกับผู้ประกอบการด้วย null coalescing - บทความ MSDN

หากคุณตรวจสอบmore than nullableเงื่อนไขเป็นทางเลือกคุณสามารถใช้ผู้ประกอบการ Ternary

ผู้ประกอบการที่สาม

นอกจากนี้คุณยังอาจมอง: ผู้ประกอบการ มันถูกเรียกว่าTernary หรือผู้ประกอบการตามเงื่อนไข ตัวดำเนินการตามเงื่อนไข (? :) ส่งคืนหนึ่งในสองค่าขึ้นอยู่กับค่าของนิพจน์บูลีน

ประเภท nullable สามารถมีค่าหรือสามารถ undefined ?? โอเปอเรเตอร์กำหนดค่าเริ่มต้นที่จะส่งคืนเมื่อกำหนดประเภทที่สามารถลบล้างได้ให้เป็นประเภทที่ไม่สามารถยกเลิกได้ หากคุณพยายามที่จะกำหนดประเภทค่าที่เป็นโมฆะให้เป็นประเภทค่าที่ไม่เป็นโมฆะโดยไม่ต้องใช้ ?? ผู้ประกอบการคุณจะสร้างข้อผิดพลาดในการรวบรวมเวลา ถ้าคุณใช้เพี้ยนและชนิดของค่า nullable ไม่ได้กำหนดในปัจจุบันข้อยกเว้น InvalidOperationException จะถูกส่งออกไป

ตัวอย่างรหัสจากMSDN -?: ผู้ประกอบการ (อ้างอิง C #) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

myObject = (myObject == null) ? new myClass() : myObjectดังนั้นการตัวอย่างจากคำถามและใช้ตัวดำเนินการตามเงื่อนไขที่เราต้องการมีบางอย่างเช่น ดังนั้นหากฉันไม่มีการใช้งานโอเปอเรเตอร์ที่มีเงื่อนไขดีกว่าดูเหมือนว่าจะมี "ปัญหา" เดียวกันกับการตั้งค่าออบเจกต์ให้กับตัวเอง
grin0048

หากคุณกำลังตรวจสอบมากกว่าหนึ่งเงื่อนไข (ไม่ใช่โมฆะและมากกว่าศูนย์) - ผู้ประกอบการที่มีเงื่อนไขจะทำเช่นนั้น อย่างไรก็ตามการตรวจสอบโมฆะจะใช้งานได้หรือไม่ ผู้ประกอบการ
Yusubov

2

ฉันไม่คิดว่าสถานการณ์เป็นเรื่องปกติ (หรืออย่างน้อยควรเป็น)

หากคุณต้องการให้ค่าเริ่มต้นของบางฟิลด์ไม่เป็นnullเช่นนั้นให้ตั้งค่าในตัวเริ่มต้นฟิลด์:

myClass myObject = new myClass();

หรือหากการเริ่มต้นมีความซับซ้อนมากขึ้นให้กำหนดไว้ในตัวสร้าง

หากคุณต้องการสร้างmyClassเฉพาะเมื่อคุณต้องการ (เช่นเนื่องจากการสร้างมันใช้เวลานาน) จากนั้นคุณสามารถใช้Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(สิ่งนี้เรียกว่า constructor เริ่มต้นหากการกำหนดค่าเริ่มต้นมีความซับซ้อนมากขึ้นให้ส่งแลมบ์ดาที่สร้างmyClassไปยังLazy<T>constructor)

ในการเข้าถึงค่าใช้ซึ่งจะเรียกการเริ่มต้นถ้านี้เป็นครั้งแรกที่คุณเข้าถึงmyObject.ValuemyObject.Value


การสร้างขี้เกียจ IMO จะมีประโยชน์เฉพาะในกรณีที่คุณไม่รับประกันว่าจะต้องใช้วัตถุที่เป็นผลลัพธ์เพียงครั้งเดียวที่ฉันใช้การสร้างแบบขี้เกียจคือเมื่อฉันมีผลลัพธ์ที่ได้รับเงินสดที่ฉันรีเซ็ตเมื่อมีการเปลี่ยนแปลงบางอย่างและฉันรู้ว่า และการคำนวณใหม่นั้นมีราคาแพง ฉลาดอื่น ๆ ฉันไม่ต้องการที่จะรำคาญกับการตรวจสอบเป็นโมฆะ
วงล้อประหลาด

@ ratchetfreak ใช่นั่นเป็นเหตุผลที่ฉันแนะนำให้ initializer ฟิลด์ก่อน
svick

มีกรณีอื่นด้วย สิ่งที่ฉันกำลังดูอยู่ในขณะนี้: บัตรผ่านชุดแรกเป็นกรณียกเว้น บัตรผ่านชุดที่สองจะเป็นค่าเริ่มต้นสำหรับทุกสิ่งอื่น ?? = จะตัดเส้นครึ่ง
Loren Pechtel

1

ฉันชอบใช้??ร่วมกับพารามิเตอร์เมธอดเพิ่มเติม ฉัน จำกัด ความต้องการโอเวอร์โหลดและทำให้แน่ใจว่าสิ่งนั้นมีค่า

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.