เหตุใดจึงใช้วิธีการทางเวทมนตร์ใน C #


16

ใน C # ฉันเริ่มเห็นวิธีการมายากลเหล่านี้ทั้งหมดโผล่ขึ้นมาโดยไม่ต้องสำรองข้อมูลโดยอินเทอร์เฟซ ทำไมถึงถูกเลือก?

ให้ฉันอธิบาย

ก่อนหน้านี้ใน C # หากวัตถุใช้IEnumerableอินเตอร์เฟสมันจะforeachวนซ้ำโดยอัตโนมัติ นั่นทำให้รู้สึกถึงฉันเนื่องจากมีการสำรองข้อมูลโดยอินเทอร์เฟซและถ้าฉันต้องมีIteratorฟังก์ชันของตัวเองในชั้นเรียนที่ถูกทำซ้ำฉันสามารถทำได้โดยไม่ต้องกังวลว่ามันจะมีความหมายอย่างอื่นอย่างน่าอัศจรรย์

เห็นได้ชัดว่าตอนนี้ (ไม่แน่ใจว่าเมื่อใด) อินเตอร์เฟสเหล่านี้ไม่จำเป็นอีกต่อไป เพียงแค่ต้องมีการแปลงชื่อที่ถูกต้อง

อีกตัวอย่างหนึ่งคือการทำให้ awaitable วัตถุใด ๆ โดยมีวิธีการตั้งชื่อตรง GetAwaiterที่มีคุณสมบัติเฉพาะไม่กี่

ทำไมไม่สร้างอินเทอร์เฟซอย่างที่พวกเขาทำกับIEnumerableหรือINotifyPropertyChangedสำรอง "เวทย์มนตร์" นี้ขึ้นมาแบบคงที่?

รายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่ฉันหมายถึงที่นี่:

http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/

อะไรคือข้อดีและข้อเสียของวิธีการเวทย์มนตร์และมีที่ไหนออนไลน์ที่ฉันสามารถหาอะไรเกี่ยวกับสาเหตุการตัดสินใจเหล่านี้?


4
คุณควรแก้ไขความเห็นส่วนตัวของคุณว่าทำไม "เวทมนต์ไม่ดี" ถ้าคุณไม่ต้องการถูกปิด
DougM

2
หากคุณต้องการทราบว่าทำไม Anders Hejlsberg ทำเช่นนั้นคุณจะต้องถามเขา ฉันสามารถบอกคุณได้ว่าทำไมฉันถึงต้องทำอย่างนั้น วิธีการขยายช่วยให้คุณสามารถเพิ่มวิธีการ "ปลอม" ในประเภทที่มีอยู่ แต่ไม่มีส่วนต่อประสานส่วนขยาย หากคุณต้องการอินเทอร์เฟซสำหรับพูดasync/ awaitแล้วนั่นจะใช้ได้กับโค้ดที่เขียนหลังจาก. NET 4.5 กลายเป็นที่แพร่หลายมากพอที่จะเป็นเป้าหมายที่ทำงานได้…ซึ่งโดยทั่วไปแล้วในตอนนี้ แต่การแปลวากยสัมพันธ์อย่างหมดจดเป็นการเรียกเมธอดทำให้ฉันสามารถเพิ่มawaitฟังก์ชันการทำงานให้กับประเภทที่มีอยู่หลังจากข้อเท็จจริง
Jörg W Mittag

2
โปรดทราบว่านี่คือการใช้การวางแนววัตถุโดยทั่วไป: ตราบใดที่วัตถุตอบสนองต่อข้อความที่เหมาะสมจะถือว่าเป็นประเภทที่ถูกต้อง
Jörg W Mittag

7
"ก่อนหน้านี้" และ "ตอนนี้" ของคุณล้าหลัง - มีการใช้วิธีเวทย์มนตร์เพื่อใช้เป็นฟังก์ชันพื้นฐานสำหรับforeachวนลูปในตอนแรก มีไม่เคยได้รับความต้องการสำหรับวัตถุที่จะใช้IEnumerableสำหรับforeachการทำงาน เป็นเพียงการประชุมที่จะทำเช่นนั้น
Jesse C. Slicer

2
@ gbulmer นี่คือที่ที่ฉันจำได้ว่าได้รับแนวคิดจาก: blogs.msdn.com/b/ericlippert/archive/2011/06/30/… (และในระดับที่น้อยกว่าericlippert.com/2013/07/22/22 )
Jesse C. Slicer

คำตอบ:


16

โดยทั่วไปแล้ว“ วิธีการทางเวทมนตร์” จะใช้เมื่อไม่สามารถสร้างส่วนต่อประสานที่ใช้งานได้ในลักษณะเดียวกัน

เมื่อforeachได้รับการแนะนำครั้งแรกใน C # 1.0 (พฤติกรรมนั้นไม่ได้เป็นสิ่งที่เกิดขึ้นเมื่อเร็ว ๆ นี้) มันต้องใช้วิธีการทางเวทย์มนตร์ ตัวเลือกโดยทั่วไปคือ:

  1. ใช้ที่ไม่ใช่แบบทั่วไปIEnumerableและใช้IEnumeratorงานได้กับobjects ซึ่งหมายถึงประเภทของมวย เนื่องจากการวนซ้ำบางรายการเช่นรายการintควรเร็วมากและไม่ควรสร้างค่าจำนวนมากอย่างขยะนี่ไม่ใช่ทางเลือกที่ดี

  2. รอยาชื่อสามัญ นี่อาจหมายถึงการหน่วงเวลา. Net 1.0 (หรืออย่างน้อยforeach) นานกว่า 3 ปี

  3. ใช้วิธีการเวทย์มนตร์

ดังนั้นพวกเขาจึงเลือกตัวเลือก # 3 และอยู่กับเราด้วยเหตุผลด้านความเข้ากันได้ย้อนหลังแม้ว่าตั้งแต่. Net 2.0 IEnumerable<T>จะต้องใช้งานได้เช่นกัน


การกำหนดค่าเริ่มต้นของการรวบรวมสามารถดูแตกต่างกันในแต่ละประเภทการรวบรวม เปรียบเทียบList<T>:

public void Add(T item)

และDictionary<TKey, TValue>:

public void Add(TKey key, TValue value)

คุณไม่มีอินเทอร์เฟซเดียวที่จะรองรับเฉพาะแบบฟอร์มแรกList<T>และแบบที่สองDictionary<TKey, TValue>เท่านั้น


โดยปกติแล้ววิธีการของ LINQ จะถูกนำมาใช้เป็นวิธีการขยาย (เพื่อให้สามารถใช้งานได้เพียงครั้งเดียวเช่น LINQ ไปยังวัตถุสำหรับทุกประเภทที่นำไปใช้IEnumerable<T>) ซึ่งหมายความว่ามันเป็นไปไม่ได้ที่จะใช้อินเทอร์เฟซ


สำหรับawaitการGetResult()วิธีการสามารถกลับมาอย่างใดอย่างหนึ่งหรือบางประเภทvoid Tอีกครั้งคุณไม่มีอินเทอร์เฟซเดียวที่สามารถจัดการได้ แม้ว่าจะawaitเป็นบางส่วนที่ใช้อินเทอร์เฟซ: ผู้รอคอยต้องดำเนินการINotifyCompletionและสามารถนำICriticalNotifyCompletionไปใช้ได้


1
คุณแน่ใจหรือว่าพวกเขาเลือกตัวเลือก # 3 สำหรับ C # 1.0? ความทรงจำของฉันคือมันเป็นตัวเลือกที่ 1 หน่วยความจำของฉันคือ C # อ้างว่าเหนือกว่าอย่างมีนัยสำคัญมากกว่า Java เพราะคอมไพเลอร์ C # ทำ 'auto-boxing' และ 'auto-unboxing' มันถูกอ้างว่า, C # ทำรหัสเช่นนั้น foreach ทำงานได้ดีกว่า Java ส่วนหนึ่งเป็นเพราะนั้น
gbulmer

1
เอกสารสำหรับforeachใน C # 1.2 (ไม่มีสิ่งใดใน MSDN สำหรับ C # 1.0) กล่าวว่านิพจน์ในforeach“ ประเมินเป็นประเภทที่ใช้IEnumerableหรือประเภทที่ประกาศGetEnumeratorวิธีการ” และฉันไม่แน่ใจว่าคุณหมายถึงสิ่งนั้นโดยการแกะกล่องออก ใน C # ชัดเจนเสมอ
svick

1
@gbulmer และข้อกำหนด ECMA สำหรับ C # ตั้งแต่เดือนธันวาคม 2544 (ซึ่งต้องหมายความว่าสำหรับ C # 1.0) ยังกล่าวอีกว่า (§15.8.4):“ ประเภท C ถูกกล่าวว่าเป็นประเภทคอลเลกชันหากใช้อินเทอร์เฟซ System.IEnumerable หรือใช้รูปแบบการรวบรวมโดยปฏิบัติตามเกณฑ์ทั้งหมดต่อไปนี้ […]”
svick

1
@svick foreach(T x in sequence)ใช้การปลดเปลื้องที่ชัดเจนTกับองค์ประกอบของลำดับ ดังนั้นถ้าลำดับเป็นแบบธรรมดาIEnumerableและTเป็นประเภทค่ามันจะไม่ทำกล่องโดยไม่ต้องมีการส่งนักแสดงที่ชัดเจนในโค้ดของคุณ หนึ่งในส่วนที่น่าเกลียดที่สุดของ C #
CodesInChaos

@CodesInChaos "auto-unboxing" ฟังดูค่อนข้างทั่วไปฉันไม่ได้ตระหนักว่ามันอ้างอิงคุณสมบัติเฉพาะนี้ (ผมว่าผมควรจะมีตั้งแต่ที่เราได้พูดคุยเกี่ยวกับforeach.)
svick
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.