เหตุใดคำสั่งมอบหมายจึงส่งคืนค่า


127

สิ่งนี้ได้รับอนุญาต:

int a, b, c;
a = b = c = 16;

string s = null;
while ((s = "Hello") != null) ;

ตามความเข้าใจของฉันการมอบหมายs = ”Hello”;ควรทำให้“Hello”ถูกมอบหมายให้sเท่านั้น แต่การดำเนินการไม่ควรส่งคืนค่าใด ๆ ถ้าเป็นจริงก็((s = "Hello") != null)จะเกิดข้อผิดพลาดเนื่องจากnullจะเปรียบเทียบกับอะไร

อะไรคือเหตุผลเบื้องหลังการอนุญาตให้คำสั่งมอบหมายส่งคืนค่า


45
สำหรับการอ้างอิงลักษณะการทำงานนี้ถูกกำหนดไว้ในข้อมูลจำเพาะ C # : "ผลลัพธ์ของนิพจน์การกำหนดอย่างง่ายคือค่าที่กำหนดให้กับตัวถูกดำเนินการด้านซ้ายผลลัพธ์มีประเภทเดียวกับตัวถูกดำเนินการด้านซ้ายและถูกจัดประเภทเป็นค่าเสมอ"
Michael Petrotta

1
@Skurmedel ฉันไม่ได้เป็นผู้โหวตลงคะแนนฉันเห็นด้วยกับคุณ แต่อาจเพราะคิดว่าเป็นคำถามเชิงโวหาร?
jsmith

ในการกำหนด pascal / delphi: = ไม่ส่งคืนอะไรเลย ฉันเกลียดมัน.
Andrey

20
มาตรฐานสำหรับภาษาในสาขา C Assignments คือนิพจน์
Hans Passant

"การมอบหมาย ... ควรทำให้" สวัสดี "ถูกกำหนดให้กับ s เท่านั้น แต่การดำเนินการไม่ควรส่งคืนค่าใด ๆ " - ทำไม? "อะไรคือเหตุผลเบื้องหลังการอนุญาตให้คำสั่งมอบหมายส่งคืนค่า" - เนื่องจากมีประโยชน์ดังตัวอย่างของคุณเอง (ตัวอย่างที่สองของคุณงี่เง่า แต่while ((s = GetWord()) != null) Process(s);ไม่ใช่)
Jim Balter

คำตอบ:


160

เพื่อความเข้าใจของฉันการมอบหมาย s = "สวัสดี"; ควรทำให้ "สวัสดี" ถูกกำหนดให้กับ s เท่านั้น แต่การดำเนินการไม่ควรส่งคืนค่าใด ๆ

ความเข้าใจของคุณไม่ถูกต้อง 100% คุณอธิบายได้ไหมว่าทำไมคุณถึงเชื่อเรื่องเท็จนี้?

อะไรคือเหตุผลเบื้องหลังการอนุญาตให้คำสั่งมอบหมายส่งคืนค่า

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

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

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

เหตุผลที่อยู่เบื้องหลังการอนุญาตคุณสมบัตินี้เป็นเพราะ (1) มักจะสะดวกและ (2) เป็นสำนวนในภาษาคล้ายซี

อาจทราบว่ามีการถามคำถาม: เหตุใดสำนวนนี้จึงเป็นภาษาเหมือน C?

Dennis Ritchie ไม่สามารถถามได้อีกต่อไปน่าเสียดาย แต่ฉันเดาว่างานที่มอบหมายมักจะทิ้งคุณค่าที่เพิ่งกำหนดไว้ในทะเบียน C เป็นภาษาที่ "ใกล้เคียงกับเครื่อง" มาก ดูเหมือนว่าเป็นไปได้และสอดคล้องกับการออกแบบของ C ว่ามีคุณลักษณะภาษาซึ่งโดยพื้นฐานแล้วหมายถึง "ใช้ค่าที่ฉันเพิ่งกำหนดต่อไป" มันง่ายมากที่จะเขียนตัวสร้างโค้ดสำหรับคุณสมบัตินี้ คุณเพียงแค่ใช้การลงทะเบียนที่เก็บค่าที่กำหนดไว้


1
ไม่ทราบว่ามีสิ่งใดที่ส่งคืนค่า (แม้แต่การสร้างอินสแตนซ์ก็ถือว่าเป็นนิพจน์)
user437291

9
@ user437291: หากการสร้างอินสแตนซ์ไม่ใช่นิพจน์คุณจะไม่สามารถทำอะไรกับอ็อบเจ็กต์ที่สร้างขึ้น - คุณไม่สามารถกำหนดให้กับบางสิ่งบางอย่างคุณไม่สามารถส่งผ่านมันไปยังเมธอดและคุณไม่สามารถเรียกใช้เมธอดใด ๆ กับมัน มันจะไม่มีประโยชน์เลยใช่ไหม
Timwi

1
การเปลี่ยนตัวแปรเป็น "เพียง" ผลข้างเคียงของตัวดำเนินการกำหนดซึ่งการสร้างนิพจน์จะให้ค่าเป็นจุดประสงค์หลัก
mk12

2
"ความเข้าใจของคุณไม่ถูกต้อง 100%" - แม้ว่าการใช้ภาษาอังกฤษของ OP จะไม่ได้มากที่สุด แต่ "ความเข้าใจ" ของเขาก็เกี่ยวกับสิ่งที่ควรจะเป็นในกรณีนี้ทำให้มีความเห็นดังนั้นจึงไม่ใช่เรื่องที่ควรจะเป็น "ไม่ถูกต้อง 100%" . นักออกแบบภาษาบางคนเห็นด้วยกับคำว่า "should" ของ OP และทำให้การมอบหมายงานไม่มีคุณค่าหรือห้ามไม่ให้เป็นนิพจน์ย่อย ผมคิดว่าเป็นจิตไป=/ ==พิมพ์ผิดซึ่งเป็น addressed ได้อย่างง่ายดายโดยไม่อนุญาตให้ใช้ค่าของ=จนกว่าจะมีการ parenthesed เช่นif ((x = y))หรือif ((x = y) == true)ได้รับอนุญาต แต่if (x = y)ไม่ได้รับอนุญาต
Jim Balter

"ไม่ทราบว่าสิ่งใดที่ส่งคืนค่า ... ถือเป็นนิพจน์" - อืม ... ทุกสิ่งที่ส่งคืนค่าจะถือเป็นนิพจน์ - ทั้งสองมีความหมายเหมือนกัน
Jim Balter

44

คุณยังไม่ได้ให้คำตอบ? เพื่อเปิดใช้งานโครงสร้างที่คุณกล่าวถึง

กรณีทั่วไปที่ใช้คุณสมบัตินี้ของตัวดำเนินการกำหนดคือการอ่านบรรทัดจากไฟล์ ...

string line;
while ((line = streamReader.ReadLine()) != null)
    // ...

1
+1 นี่จะต้องเป็นหนึ่งในการใช้งานจริงที่สุดของโครงสร้างนี้
Mike Burton

11
ฉันชอบมันมากสำหรับตัวเริ่มต้นคุณสมบัติที่เรียบง่าย แต่คุณต้องระวังและขีดเส้นให้ "ง่าย" ต่ำเพื่อความสามารถในการอ่าน: return _myBackingField ?? (_myBackingField = CalculateBackingField());แกลบน้อยกว่าการตรวจสอบค่าว่างและการกำหนด
Tom Mayfield

35

การใช้นิพจน์การมอบหมายที่ฉันชอบคือสำหรับคุณสมบัติที่เริ่มต้นอย่างเฉื่อยชา

private string _name;
public string Name
{
    get { return _name ?? (_name = ExpensiveNameGeneratorMethod()); }
}

5
ซึ่งเป็นเหตุผลว่าทำไมหลาย ๆ คนจึงแนะนำ??=ไวยากรณ์
กำหนดค่า

27

ประการแรกช่วยให้คุณสามารถเชื่อมโยงงานที่มอบหมายได้ดังตัวอย่าง:

a = b = c = 16;

สำหรับอีกรายการหนึ่งจะช่วยให้คุณกำหนดและตรวจสอบผลลัพธ์ในนิพจน์เดียว:

while ((s = foo.getSomeString()) != null) { /* ... */ }

ทั้งสองอย่างอาจมีเหตุผลที่น่าสงสัย แต่ก็มีคนที่ชอบโครงสร้างเหล่านี้แน่นอน


5
+1 Chaining ไม่ใช่คุณสมบัติที่สำคัญ แต่ก็น่ายินดีที่ได้เห็นว่า "ใช้งานได้จริง" เมื่อหลาย ๆ อย่างไม่เป็นเช่นนั้น
Mike Burton

+1 สำหรับการผูกมัดด้วย ฉันมักจะคิดว่าเป็นกรณีพิเศษที่ใช้ภาษามากกว่าผลข้างเคียงตามธรรมชาติของ "นิพจน์คืนค่า"
Caleb Bell

2
นอกจากนี้ยังช่วยให้คุณสามารถใช้การมอบหมายในการส่งคืน:return (HttpContext.Current.Items["x"] = myvar);
Serge Shultz

14

นอกเหนือจากเหตุผลที่กล่าวไปแล้ว (การผูกมัดการกำหนดชุดและการทดสอบภายใน while ลูป ฯลฯ ) เพื่อใช้คำสั่งอย่างถูกต้องusingคุณต้องมีคุณสมบัตินี้:

using (Font font3 = new Font("Arial", 10.0f))
{
    // Use font3.
}

MSDN ไม่สนับสนุนการประกาศวัตถุที่ใช้แล้วทิ้งภายนอกคำสั่งใช้เนื่องจากจะยังคงอยู่ในขอบเขตแม้ว่าจะถูกกำจัดไปแล้วก็ตาม (ดูบทความ MSDN ที่ฉันเชื่อมโยง)


4
ฉันไม่คิดว่านี่เป็นการผูกมัดการมอบหมายงานจริงๆ
kenny

1
@kenny: เอ่อ ... ไม่ แต่ฉันไม่เคยอ้างว่าเป็นเช่นกัน ดังที่ฉันได้กล่าวไปแล้วนอกเหนือจากเหตุผลที่กล่าวไปแล้วซึ่งรวมถึงการผูกมัดการมอบหมาย - คำสั่งการใช้งานจะใช้งานได้ยากกว่ามากไม่ใช่เพราะข้อเท็จจริงที่ว่าตัวดำเนินการมอบหมายจะส่งคืนผลลัพธ์ของการดำเนินการมอบหมาย สิ่งนี้ไม่เกี่ยวข้องกับการผูกมัดการมอบหมายงานเลย
Martin Törnwall

1
แต่ดังที่ Eric ระบุไว้ข้างต้น "คำสั่งมอบหมายไม่ส่งคืนค่า" นี่เป็นเพียงไวยากรณ์ภาษาของคำสั่งใช้ซึ่งพิสูจน์ได้ว่าไม่สามารถใช้ "นิพจน์การกำหนด" ในคำสั่งที่ใช้
kenny

@kenny: ขอโทษคำศัพท์ของฉันผิดอย่างชัดเจน ฉันตระหนักดีว่าคำสั่งการใช้สามารถนำไปใช้งานได้โดยที่ภาษาไม่ได้รับการสนับสนุนทั่วไปสำหรับนิพจน์การมอบหมายที่ส่งคืนค่า ในสายตาของฉันจะไม่สอดคล้องกันอย่างมาก
Martin Törnwall

2
@kenny: อันที่จริงเราสามารถใช้คำจำกัดความและการกำหนดหรือนิพจน์ใดก็ได้ในusing. using (X x = new X())ทั้งหมดเหล่านี้เป็นทางกฎหมาย: using (x = new X()), using (x), อย่างไรก็ตามในตัวอย่างนี้เนื้อหาของคำสั่งการใช้เป็นไวยากรณ์พิเศษที่ไม่ได้ขึ้นอยู่กับการกำหนดค่าที่ส่งคืนค่า - Font font3 = new Font("Arial", 10.0f)ไม่ใช่นิพจน์และไม่ถูกต้องในทุกที่ที่ต้องการนิพจน์
กำหนดค่า

10

ฉันต้องการอธิบายรายละเอียดเกี่ยวกับประเด็นเฉพาะที่ Eric Lippert ทำไว้ในคำตอบของเขาและให้ความสำคัญกับโอกาสเฉพาะที่ไม่มีใครแตะต้องเลย เอริคกล่าวว่า:

[... ] งานที่มอบหมายมักจะทิ้งค่าที่เพิ่งกำหนดไว้ในทะเบียน

ฉันอยากจะบอกว่างานจะทิ้งค่าที่เราพยายามกำหนดให้กับตัวถูกดำเนินการด้านซ้ายของเราเสมอ ไม่ใช่แค่ "เกือบตลอดเวลา" แต่ฉันไม่รู้เพราะฉันไม่พบปัญหานี้ที่แสดงความคิดเห็นไว้ในเอกสาร ในทางทฤษฎีแล้วอาจเป็นขั้นตอนการปฏิบัติที่มีประสิทธิภาพมากในการ "ละทิ้ง" และไม่ประเมินค่าตัวถูกดำเนินการด้านซ้ายอีกครั้ง แต่มีประสิทธิภาพหรือไม่?

'มีประสิทธิภาพ' ใช่สำหรับตัวอย่างทั้งหมดที่สร้างขึ้นในคำตอบของชุดข้อความนี้ แต่มีประสิทธิภาพในกรณีของคุณสมบัติและดัชนีที่ใช้ get- และ set accessors? ไม่ใช่เลย. พิจารณารหัสนี้:

class Test
{
    public bool MyProperty { get { return true; } set { ; } }
}

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

Test test = new Test();

if ((test.MyProperty = false) == true)
    Console.WriteLine("Please print this text.");

else
    Console.WriteLine("Unexpected!!");

เดาว่ามันพิมพ์อะไร? Unexpected!!มันพิมพ์ ปรากฎว่า set accessor ถูกเรียกอย่างแท้จริงซึ่งไม่ได้ทำอะไรเลย แต่หลังจากนั้นจะไม่มีการเรียก get accessor เลย การมอบหมายนั้นทิ้งfalseคุณค่าที่เราพยายามกำหนดให้กับทรัพย์สินของเรา และfalseค่านี้คือสิ่งที่คำสั่ง if ประเมิน

ฉันจะจบด้วยตัวอย่างโลกแห่งความเป็นจริงที่ช่วยให้ฉันค้นคว้าปัญหานี้ ฉันสร้างตัวสร้างดัชนีซึ่งเป็นกระดาษห่อหุ้มที่สะดวกสำหรับคอลเลกชัน ( List<string>) ที่คลาสของฉันมีเป็นตัวแปรส่วนตัว

พารามิเตอร์ที่ส่งไปยังตัวสร้างดัชนีคือสตริงซึ่งจะถือว่าเป็นค่าในคอลเล็กชันของฉัน get accessor จะส่งคืนจริงหรือเท็จหากมีค่านั้นอยู่ในรายการหรือไม่ ดังนั้น get accessor จึงเป็นอีกวิธีหนึ่งในการใช้List<T>.Containsวิธีนี้

ถ้าผู้เข้าถึงชุดของดัชนีถูกเรียกด้วยสตริงเป็นอาร์กิวเมนต์และตัวถูกดำเนินการด้านขวาคือบูtrueลเขาจะเพิ่มพารามิเตอร์นั้นในรายการ แต่ถ้าพารามิเตอร์เดียวกันถูกส่งไปยัง accessor และตัวถูกดำเนินการด้านขวาคือบูfalseลเขาจะลบองค์ประกอบออกจากรายการแทน ดังนั้นการเข้าถึงชุดถูกใช้เป็นทางเลือกที่สะดวกทั้งสองและList<T>.AddList<T>.Remove

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

if (myObject["stringValue"] = true)
    ; // Set operation succeeded..!

แต่ดังตัวอย่างก่อนหน้านี้ของฉันแสดงให้เห็นว่า get accessor ซึ่งควรจะดูว่าค่าอยู่ในรายการนั้นไม่ได้ถูกเรียกด้วยซ้ำ trueค่าถูกทิ้งไว้ข้างหลังเสมอได้อย่างมีประสิทธิภาพทำลายสิ่งตรรกะที่ผมได้นำมาใช้ในการเข้าถึงของฉันได้รับ


คำตอบที่น่าสนใจมาก และมันทำให้ฉันคิดในแง่ดีเกี่ยวกับการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ
Elliot

1
แม้ว่าสิ่งนี้จะน่าสนใจ แต่ก็เป็นความคิดเห็นเกี่ยวกับคำตอบของ Eric ไม่ใช่คำตอบสำหรับคำถามของ OP
Jim Balter

ฉันไม่คาดหวังว่านิพจน์การกำหนดที่พยายามจะได้รับค่าของคุณสมบัติเป้าหมายก่อน หากตัวแปรไม่แน่นอนและประเภทตรงกันเหตุใดจึงมีความสำคัญว่าค่าเดิมคืออะไร เพียงตั้งค่าใหม่ ฉันไม่ใช่แฟนของงานมอบหมายในการทดสอบ IF มันกลับมาเป็นจริงเพราะการกำหนดสำเร็จหรือเป็นค่าบางอย่างเช่นจำนวนเต็มบวกที่บังคับให้เป็นจริงหรือเป็นเพราะคุณกำหนดบูลีน ตรรกะคลุมเครือ
Davos

6

หากการมอบหมายไม่ส่งคืนค่าบรรทัดa = b = c = 16จะไม่ทำงานเช่นกัน

นอกจากนี้ความสามารถในการเขียนสิ่งต่างๆเช่นwhile ((s = readLine()) != null)อาจมีประโยชน์ในบางครั้ง

ดังนั้นเหตุผลที่ปล่อยให้การมอบหมายคืนค่าที่กำหนดคือให้คุณทำสิ่งเหล่านั้น


ไม่มีใครกล่าวถึงข้อเสีย - ฉันมาที่นี่โดยสงสัยว่าเหตุใดการมอบหมายจึงไม่สามารถคืนค่าว่างได้เพื่อที่การมอบหมายทั่วไปเทียบกับข้อผิดพลาดการเปรียบเทียบ 'if (something = 1) {}' จะไม่สามารถรวบรวมได้แทนที่จะส่งกลับจริงเสมอ ตอนนี้ฉันเห็นแล้วว่าจะสร้าง ... ประเด็นอื่น ๆ !
จอน

1
@ จอนนั้นสามารถป้องกันได้อย่างง่ายดายโดยการไม่อนุญาตหรือเตือนเกี่ยวกับการ=เกิดขึ้นในนิพจน์ที่ไม่ได้อยู่ในวงเล็บ (ไม่นับ parens ของคำสั่ง if / while เอง) gcc ให้คำเตือนดังกล่าวและได้กำจัดข้อบกพร่องระดับนี้ออกจากโปรแกรม C / C ++ ที่คอมไพล์ด้วย เป็นเรื่องน่าเสียดายที่นักเขียนคอมไพเลอร์คนอื่น ๆ ให้ความสนใจกับเรื่องนี้และแนวคิดดีๆอีกมากมายใน gcc
Jim Balter

4

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

s = "Hello"; //s now contains the value "Hello"
(s != null) //returns true.

ตามที่คนอื่น ๆ ได้ชี้ให้เห็นผลลัพธ์ของการมอบหมายคือคุณค่าที่ได้รับมอบหมาย ฉันพบว่ามันยากที่จะจินตนาการถึงข้อดีของการมี

((s = "Hello") != null)

และ

s = "Hello";
s != null;

ไม่เทียบเท่า ...


ในขณะที่คุณเข้าใจถูกแล้วว่าทั้งสองบรรทัดของคุณเทียบเท่ากับคำแถลงของคุณว่างานที่มอบหมายไม่ได้ส่งคืนค่านั้นไม่เป็นความจริงในทางเทคนิค ความเข้าใจของฉันคือผลลัพธ์ของการมอบหมายคือค่า (ตามข้อกำหนด C #) และในแง่นั้นก็ไม่ต่างจากการพูดตัวดำเนินการเพิ่มเติมในนิพจน์เช่น 1 + 2 + 3
Steve Ellinger

3

ฉันคิดว่าเหตุผลหลักคือความคล้ายคลึงกัน (โดยเจตนา) กับ C ++ และ C ทำให้ตัวดำเนินการการกำหนด (และโครงสร้างภาษาอื่น ๆ จำนวนมาก) ทำตัวเหมือนคู่ C ++ ของพวกเขาเพียงแค่ทำตามหลักการที่น่าประหลาดใจน้อยที่สุดและโปรแกรมเมอร์ที่มาจากหยิกอื่น - ภาษาวงเล็บสามารถใช้ได้โดยไม่ต้องใช้ความคิดมาก การเลือกโปรแกรมเมอร์ C ++ ได้ง่ายเป็นหนึ่งในเป้าหมายหลักในการออกแบบ C #


2

ด้วยเหตุผลสองประการที่คุณระบุไว้ในโพสต์ของคุณ
1) เพื่อให้คุณทำได้a = b = c = 16
2) เพื่อให้คุณสามารถทดสอบว่างานสำเร็จหรือไม่ if ((s = openSomeHandle()) != null)


1

ความจริงที่ว่า 'a ++' หรือ 'printf ("foo")' อาจมีประโยชน์ไม่ว่าจะเป็นคำสั่งที่มีอยู่ในตัวเองหรือเป็นส่วนหนึ่งของนิพจน์ที่ใหญ่กว่าหมายความว่า C ต้องยอมให้มีความเป็นไปได้ที่ผลลัพธ์ของนิพจน์อาจจะเป็นหรือไม่ก็ได้ ใช้ เนื่องจากมีความคิดทั่วไปว่านิพจน์ที่อาจ 'คืนค่า' ค่าที่เป็นประโยชน์ก็อาจทำได้เช่นกัน การเชื่อมโยงการมอบหมายงานอาจ "น่าสนใจ" เล็กน้อยในภาษา C และน่าสนใจยิ่งขึ้นใน C ++ หากตัวแปรทั้งหมดที่เป็นปัญหาไม่มีประเภทเดียวกัน ควรหลีกเลี่ยงประเพณีดังกล่าวได้ดีที่สุด


1

อีกตัวอย่างการใช้งานที่ดีฉันใช้สิ่งนี้ตลอดเวลา:

var x = _myVariable ?? (_myVariable = GetVariable());
//for example: when used inside a loop, "GetVariable" will be called only once

0

ข้อได้เปรียบพิเศษที่ฉันไม่เห็นในคำตอบที่นี่คือไวยากรณ์สำหรับการมอบหมายจะขึ้นอยู่กับเลขคณิต

ตอนนี้x = y = b = c = 2 + 3หมายถึงสิ่งที่แตกต่างในการคำนวณมากกว่าภาษาสไตล์ C ในทางคณิตศาสตร์เป็นการยืนยันเราระบุว่า x เท่ากับ y เป็นต้นและในภาษาสไตล์ซีเป็นคำสั่งที่ทำให้ x เท่ากับ y เป็นต้นหลังจากที่ดำเนินการแล้ว

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


@JimBalter ฉันสงสัยว่าในอีกห้าปีข้างหน้าคุณอาจโต้แย้งได้จริงหรือ
จอนฮันนา

@ JimBalter ไม่ความคิดเห็นที่สองของคุณเป็นเรื่องโต้แย้งและเป็นเรื่องที่ฟังดูดี การโต้กลับความคิดของฉันเป็นเพราะความคิดเห็นแรกของคุณไม่ใช่ แต่ถึงกระนั้นเมื่อการเรียนรู้ทางคณิตศาสตร์ที่เราเรียนรู้a = b = cวิธีการที่aและbและcเป็นสิ่งเดียวกัน เมื่อเรียนรู้ภาษา C สไตล์ที่เราเรียนรู้หลังจากนั้นa = b = c, aและเป็นสิ่งเดียวกับb cความหมายมีความแตกต่างกันอย่างแน่นอนตามที่คำตอบของฉันบอก แต่ตอนยังเป็นเด็กฉันได้เรียนรู้ที่จะเขียนโปรแกรมในภาษาที่ใช้=สำหรับการมอบหมายงานครั้งแรก แต่ไม่อนุญาตให้a = b = cสิ่งนี้ดูไม่มีเหตุผลสำหรับฉันแม้ว่า ...
จอนฮันนา

…หลีกเลี่ยงไม่ได้เพราะภาษานั้นยังใช้=สำหรับการเปรียบเทียบความเท่าเทียมกันดังนั้นในนั้นa = b = cจะต้องมีความหมายa = b == cว่าในภาษาสไตล์ C ฉันพบว่าการผูกโซ่อนุญาตใน C ใช้งานง่ายกว่ามากเพราะฉันสามารถเปรียบเทียบกับเลขคณิตได้
Jon Hanna

0

ฉันชอบใช้ค่าการส่งคืนการมอบหมายเมื่อฉันต้องการอัปเดตสิ่งต่างๆมากมายและส่งคืนว่ามีการเปลี่ยนแปลงหรือไม่:

bool hasChanged = false;

hasChanged |= thing.Property != (thing.Property = "Value");
hasChanged |= thing.AnotherProperty != (thing.AnotherProperty = 42);
hasChanged |= thing.OneMore != (thing.OneMore = "They get it");

return hasChanged;

ระวังให้ดี คุณอาจคิดว่าคุณสามารถย่อให้สั้นลงได้:

return thing.Property != (thing.Property = "Value") ||
    thing.AnotherProperty != (thing.AnotherProperty = 42) ||
    thing.OneMore != (thing.OneMore = "They get it");

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

ดูhttps://dotnetfiddle.net/e05Rh8เพื่อเล่นกับสิ่งนี้

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