คำตอบสั้น ๆ :
ผู้ประกอบการอ้างเป็นผู้ประกอบการที่ก่อให้เกิดความหมายในตัวถูกดำเนินการปิดของมัน ค่าคงที่เป็นเพียงค่า
คำคมและค่าคงที่มีแตกต่างกันความหมายและดังนั้นจึงมีการแสดงที่แตกต่างกันในการแสดงออกของต้นไม้ การมีตัวแทนเหมือนกันสำหรับสองสิ่งที่แตกต่างกันมากทำให้เกิดความสับสนและเกิดข้อผิดพลาดได้ง่าย
คำตอบยาว:
พิจารณาสิ่งต่อไปนี้:
(int s)=>(int t)=>s+t
แลมบ์ดาด้านนอกเป็นโรงงานสำหรับแอดเดอร์ที่ผูกไว้กับพารามิเตอร์ของแลมบ์ดาด้านนอก
ตอนนี้สมมติว่าเราต้องการแทนค่านี้เป็นแผนภูมินิพจน์ที่จะรวบรวมและดำเนินการในภายหลัง โครงสร้างของนิพจน์ควรเป็นอย่างไร ขึ้นอยู่กับว่าคุณต้องการให้สถานะที่คอมไพล์ส่งคืนผู้รับมอบสิทธิ์หรือโครงสร้างนิพจน์
เริ่มต้นด้วยการยกเลิกกรณีที่ไม่น่าสนใจ หากเราต้องการส่งคืนผู้รับมอบสิทธิ์คำถามที่ว่าจะใช้ใบเสนอราคาหรือค่าคงที่เป็นจุดที่สงสัย:
var ps = Expression.Parameter(typeof(int), "s");
var pt = Expression.Parameter(typeof(int), "t");
var ex1 = Expression.Lambda(
Expression.Lambda(
Expression.Add(ps, pt),
pt),
ps);
var f1a = (Func<int, Func<int, int>>) ex1.Compile();
var f1b = f1a(100);
Console.WriteLine(f1b(123));
แลมด้ามีแลมด้าที่ซ้อนกัน คอมไพเลอร์สร้างแลมด้าภายในเป็นตัวแทนของฟังก์ชันที่ปิดอยู่เหนือสถานะของฟังก์ชันที่สร้างขึ้นสำหรับแลมด้าภายนอก เราไม่จำเป็นต้องพิจารณากรณีนี้อีกต่อไป
สมมติว่าเราต้องการให้สถานะที่คอมไพล์ส่งคืนโครงสร้างนิพจน์ของการตกแต่งภายใน มีสองวิธีในการดำเนินการคือวิธีที่ง่ายและวิธีที่ยาก
วิธีที่ยากคือการพูดแทน
(int s)=>(int t)=>s+t
สิ่งที่เราหมายถึงคือ
(int s)=>Expression.Lambda(Expression.Add(...
จากนั้นสร้างแผนภูมินิพจน์สำหรับสิ่งนั้นโดยสร้างระเบียบนี้ :
Expression.Lambda(
Expression.Call(typeof(Expression).GetMethod("Lambda", ...
blah blah blah รหัสสะท้อนหลายสิบบรรทัดเพื่อสร้างแลมด้า จุดประสงค์ของผู้ประกอบการอ้างคือการบอกคอมไพเลอร์ต้นไม้แสดงออกว่าเราต้องการแลมบ์ดาที่กำหนดจะถือว่าเป็นต้นไม้แสดงออกไม่เป็นฟังก์ชั่นโดยไม่ต้องสร้างอย่างชัดเจนแสดงออกรหัสรุ่นต้นไม้
วิธีง่ายๆคือ:
var ex2 = Expression.Lambda(
Expression.Quote(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f2a = (Func<int, Expression<Func<int, int>>>)ex2.Compile();
var f2b = f2a(200).Compile();
Console.WriteLine(f2b(123));
และแน่นอนถ้าคุณรวบรวมและเรียกใช้โค้ดนี้คุณจะได้รับคำตอบที่ถูกต้อง
โปรดสังเกตว่าเครื่องหมายคำพูดเป็นตัวดำเนินการที่ทำให้เกิดความหมายปิดบนแลมด้าภายในซึ่งใช้ตัวแปรด้านนอกซึ่งเป็นพารามิเตอร์ที่เป็นทางการของแลมด้าภายนอก
คำถามคือทำไมไม่กำจัด Quote และทำให้สิ่งนี้เป็นแบบเดียวกัน?
var ex3 = Expression.Lambda(
Expression.Constant(
Expression.Lambda(
Expression.Add(ps, pt),
pt)),
ps);
var f3a = (Func<int, Expression<Func<int, int>>>)ex3.Compile();
var f3b = f3a(300).Compile();
Console.WriteLine(f3b(123));
ค่าคงที่ไม่ทำให้เกิดความหมายปิด ทำไมต้อง? คุณบอกว่านี่คือการอย่างต่อเนื่อง เป็นเพียงค่านิยม ควรจะสมบูรณ์แบบเมื่อส่งมอบให้กับคอมไพเลอร์ คอมไพเลอร์ควรจะสามารถสร้างดัมพ์ของค่านั้นไปยังสแต็กที่ต้องการได้
เนื่องจากไม่มีการกระตุ้นให้เกิดการปิดหากคุณทำเช่นนี้คุณจะได้รับ "ตัวแปร" ของประเภท "System.Int32" ไม่ได้กำหนด "ข้อยกเว้นในการเรียกใช้
(นอกเหนือจาก: ฉันเพิ่งตรวจสอบตัวสร้างโค้ดสำหรับการสร้างผู้รับมอบสิทธิ์จากต้นไม้นิพจน์ที่ยกมาและน่าเสียดายที่ความคิดเห็นที่ฉันใส่ลงในโค้ดในปี 2006 ยังคงอยู่ที่นั่น FYI พารามิเตอร์ด้านนอกที่ยกขึ้นจะถูกรวมเป็นค่าคงที่เมื่อยกมา แผนภูมินิพจน์ถูก reified เป็นผู้รับมอบสิทธิ์โดยคอมไพเลอร์รันไทม์มีเหตุผลที่ดีว่าทำไมฉันจึงเขียนโค้ดในแบบที่ฉันจำไม่ได้ในขณะนี้ แต่มันมีผลข้างเคียงที่น่ารังเกียจจากการแนะนำการปิดทับค่าของพารามิเตอร์ภายนอก แทนที่จะปิดตัวแปร. เห็นได้ชัดว่าทีมที่สืบทอดรหัสนั้นตัดสินใจที่จะไม่แก้ไขข้อบกพร่องนั้นดังนั้นหากคุณอาศัยการกลายพันธุ์ของพารามิเตอร์ภายนอกแบบปิดที่สังเกตได้ในแลมด้าภายในที่รวบรวมมาคุณจะต้องผิดหวัง อย่างไรก็ตามเนื่องจากเป็นการฝึกการเขียนโปรแกรมที่ค่อนข้างแย่สำหรับทั้ง (1) การกลายพันธุ์พารามิเตอร์ที่เป็นทางการและ (2) อาศัยการกลายพันธุ์ของตัวแปรภายนอกฉันขอแนะนำให้คุณเปลี่ยนโปรแกรมของคุณเพื่อไม่ใช้วิธีการเขียนโปรแกรมที่ไม่ดีทั้งสองนี้แทนที่จะเป็น กำลังรอการแก้ไขซึ่งดูเหมือนจะไม่เกิดขึ้น ขออภัยในความผิดพลาด)
ดังนั้นเพื่อตอบคำถามซ้ำ:
คอมไพเลอร์ C # สามารถสร้างขึ้นเพื่อรวบรวมนิพจน์แลมบ์ดาที่ซ้อนกันเป็นแผนภูมินิพจน์ที่เกี่ยวข้องกับ Expression.Constant () แทน Expression.Quote () และผู้ให้บริการแบบสอบถาม LINQ ใด ๆ ที่ต้องการประมวลผลแผนภูมินิพจน์เป็นภาษาแบบสอบถามอื่น ๆ (เช่น SQL ) สามารถมองหา ConstantExpression ที่มีประเภท Expression แทนที่จะเป็น UnaryExpression ที่มีประเภทโหนดใบเสนอราคาพิเศษและอย่างอื่นก็จะเหมือนกัน
คุณถูก. เราสามารถเข้ารหัสข้อมูลความหมายที่หมายถึง "ทำให้เกิดความหมายปิดกับค่านี้" โดยใช้ชนิดของนิพจน์คงที่เป็นแฟล็ก
จากนั้น "ค่าคงที่" จะมีความหมายว่า "ใช้ค่าคงที่นี้เว้นแต่ว่าประเภทที่เกิดขึ้นเป็นประเภทแผนภูมินิพจน์และค่าเป็นแผนภูมินิพจน์ที่ถูกต้องในกรณีนี้ให้ใช้ค่าที่เป็นโครงสร้างนิพจน์ที่เกิดจากการเขียนใหม่ ภายในของแผนภูมินิพจน์ที่กำหนดเพื่อกระตุ้นให้เกิดความหมายปิดในบริบทของ lambdas ภายนอกใด ๆ ที่เราอาจอยู่ในตอนนี้
แต่ทำไมจะเราทำสิ่งที่บ้า? ตัวดำเนินการใบเสนอราคาเป็นตัวดำเนินการที่ซับซ้อนอย่างไม่น่าเชื่อและควรใช้อย่างชัดเจนหากคุณจะใช้ คุณกำลังแนะนำว่าเพื่อเป็นการอธิบายอย่างชัดเจนเกี่ยวกับการไม่เพิ่มวิธีการโรงงานและประเภทโหนดพิเศษหนึ่งรายการจากจำนวนโหลที่มีอยู่แล้วเราจึงเพิ่มกรณีมุมที่แปลกประหลาดให้กับค่าคงที่เพื่อให้ค่าคงที่ในบางครั้งเป็นค่าคงที่เชิงตรรกะและบางครั้งก็ถูกเขียนใหม่ lambdas ที่มีความหมายปิด
มันจะมีผลค่อนข้างแปลกที่ค่าคงที่ไม่ได้หมายความว่า "ใช้ค่านี้" สมมติว่ามีเหตุผลแปลก ๆ บางอย่างที่คุณต้องการให้กรณีที่สามข้างต้นรวบรวมแผนภูมินิพจน์เป็นผู้รับมอบสิทธิ์ที่ส่งแผนภูมินิพจน์ที่มีการอ้างอิงที่ไม่ได้เขียนซ้ำไปยังตัวแปรภายนอก? ทำไม? อาจเป็นเพราะคุณกำลังทดสอบคอมไพเลอร์ของคุณและต้องการเพียงแค่ส่งผ่านค่าคงที่เพื่อที่คุณจะได้ทำการวิเคราะห์อื่น ๆ ในภายหลัง ข้อเสนอของคุณจะทำให้เป็นไปไม่ได้ ค่าคงที่ที่เกิดขึ้นเป็นประเภทต้นไม้นิพจน์จะถูกเขียนใหม่โดยไม่คำนึงถึง เรามีความคาดหวังที่สมเหตุสมผลว่า "ค่าคงที่" หมายถึง "ใช้ค่านี้" "ค่าคงที่" คือโหนด "ทำในสิ่งที่ฉันพูด" โปรเซสเซอร์คงที่ ' จะพูดตามประเภท
และโปรดทราบว่าตอนนี้คุณกำลังวางภาระในการทำความเข้าใจ (นั่นคือการเข้าใจว่าค่าคงที่มีความหมายที่ซับซ้อนซึ่งหมายถึง "ค่าคงที่" ในกรณีเดียวและ "ทำให้เกิดความหมายปิด" ตามแฟล็กที่อยู่ในระบบประเภท ) กับทุกๆผู้ให้บริการที่วิเคราะห์ความหมายของแผนภูมินิพจน์ไม่ใช่เฉพาะกับผู้ให้บริการของ Microsoft ผู้ให้บริการบุคคลที่สามเหล่านี้จะเข้าใจผิดได้อย่างไร
"ใบเสนอราคา" โบกธงสีแดงขนาดใหญ่ที่มีข้อความว่า "เฮ้เพื่อนดูนี่สิฉันเป็นรูปแลมบ์ดาที่ซ้อนกันและฉันมีความหมายแปลกประหลาดถ้าฉันปิดตัวแปรภายนอก ในขณะที่ "ค่าคงที่" กำลังพูดว่า "ฉันไม่มีค่ามากไปกว่าค่าใช้จ่ายฉันตามที่คุณเห็นสมควร" เมื่อมีบางสิ่งที่ซับซ้อนและเป็นอันตรายเราต้องการทำให้มันโบกธงสีแดงไม่ใช่ซ่อนความจริงนั้นโดยการทำให้ผู้ใช้ขุดผ่านระบบประเภทเพื่อค้นหาว่าค่านี้เป็นค่าพิเศษหรือไม่
นอกจากนี้แนวคิดที่ว่าการหลีกเลี่ยงความซ้ำซ้อนแม้จะเป็นเป้าหมายก็ไม่ถูกต้อง แน่นอนว่าการหลีกเลี่ยงความซ้ำซ้อนที่ไม่จำเป็นและสับสนเป็นเป้าหมาย แต่ความซ้ำซ้อนส่วนใหญ่เป็นสิ่งที่ดี ความซ้ำซ้อนทำให้เกิดความชัดเจน วิธีการโรงงานใหม่และโหนดชนิดมีราคาถูก เราสามารถสร้างได้มากเท่าที่เราต้องการเพื่อให้แต่ละคนแสดงถึงการดำเนินการเดียว เราไม่จำเป็นต้องใช้กลอุบายที่น่ารังเกียจเช่น "นี่หมายถึงสิ่งหนึ่งเว้นแต่ว่าช่องนี้จะถูกตั้งค่าเป็นสิ่งนี้ซึ่งในกรณีนี้จะหมายถึงอย่างอื่น"