ทำไมคลาสทั้งหมดใน. NET ที่สืบทอดจากคลาส Object?


16

มันน่าสนใจมากสำหรับฉันซึ่งข้อดีให้แนวทาง "คลาสรูทระดับโลก" สำหรับกรอบงาน กล่าวง่ายๆว่าเหตุผลอะไรที่ทำให้. NET Framework ได้รับการออกแบบให้มีคลาสรูทหนึ่งอ็อบเจ็กต์พร้อมฟังก์ชันการทำงานทั่วไปที่เหมาะสมสำหรับคลาสทั้งหมด

ทุกวันนี้เรากำลังออกแบบเฟรมเวิร์กใหม่สำหรับการใช้งานภายใน (กรอบภายใต้แพลตฟอร์ม SAP) และเราทั้งหมดแบ่งออกเป็นสองค่าย - อันดับแรกผู้ที่คิดว่ากรอบควรมีรูทส่วนกลางและที่สอง - ที่คิดตรงกันข้าม

ฉันอยู่ที่ค่าย "global root" และเหตุผลของฉันสิ่งที่วิธีการดังกล่าวจะให้ความยืดหยุ่นที่ดีและลดต้นทุนการพัฒนาทำให้เราจะไม่พัฒนาฟังก์ชั่นทั่วไปอีกต่อไป

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


6
จำไว้ว่า. NET Framework เป็นส่วนหนึ่งของสภาพแวดล้อมการพัฒนาซอฟต์แวร์ทั่วไป กรอบงานที่เฉพาะเจาะจงมากขึ้นเช่นคุณอาจไม่จำเป็นต้องมีวัตถุ "รูท" มีรากเป็นobjectในกรอบ NET ส่วนหนึ่งเป็นเพราะมันมีความสามารถขั้นพื้นฐานทั่ววัตถุทั้งหมดเช่นToString()และGetHashCode()
โรเบิร์ตฮาร์วีย์

10
"ฉันน่าสนใจมากที่รู้ว่าอะไรคือสาเหตุที่ผลักดันให้สถาปนิก. NET ออกแบบกรอบงานในลักษณะนี้" มีสามเหตุผลที่ดีมากสำหรับสิ่งนั้น: 1. Java ทำแบบนั้น, 2. Java ทำอย่างนั้นและ 3. Java ทำเช่นนั้น
dasblinkenlight

2
@vcsjones: และ Java เช่นการปนเปื้อนแล้วกับwait()/ notify()/ และnotifyAll() clone()
Joachim Sauer

3
@daskblinkenlight พี่ปู Java ไม่ได้ทำเช่นนั้น
Dante

2
@dasblinkenlight: FYI, Java เป็นหนึ่งในปัจจุบันการคัดลอก C # ด้วย lambdas, ฟังก์ชั่น LINQ ฯลฯ ...
user541686

คำตอบ:


18

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


6
นี่คือคำตอบที่ถูกต้อง การขาดการสนับสนุนทั่วไปเป็นเพียงเหตุผลเดียว อาร์กิวเมนต์ "common-methods" อื่น ๆ ไม่ใช่ข้อโต้แย้งที่แท้จริงเนื่องจากวิธีการทั่วไปไม่จำเป็นต้องเหมือนกัน - ตัวอย่าง: 1) ToString: ถูกทารุณกรรมในกรณีส่วนใหญ่ 2) GetHashCode: ขึ้นอยู่กับโปรแกรมทำคลาสส่วนใหญ่ไม่ต้องการวิธีนี้ 3) GetType: ไม่จำเป็นต้องเป็นวิธีอินสแตนซ์
Codism

2
. NET จะ "สูญเสียจุดอ่อนของความปลอดภัยทุกประเภท" โดยวิธีใด "โดย" ใช้ผิด "แนวคิดที่ว่าทุกสิ่งควรสืบทอดมาจากคลาสที่ระบุหรือไม่ มี shitcode ใดบ้างที่สามารถเขียนได้Objectซึ่งไม่สามารถเขียนได้void *ใน C ++?
Carson63000

3
ใครบอกว่าฉันคิดว่าvoid*ดีกว่า? มันไม่ใช่ ถ้าทุกอย่าง. มันยิ่งเลวร้ายลง
DeadMG

void*แย่กว่าใน C โดยที่ตัวชี้โดยนัยทั้งหมดใช้งานได้ แต่ C ++ นั้นเข้มงวดมากขึ้นในเรื่องนี้ อย่างน้อยคุณต้องส่งคำอธิบายอย่างชัดเจน
Tamás Szelei

2
@fish: คำศัพท์คลุมเครือ: ใน C ไม่มีสิ่งเช่น "นักแสดงโดยปริยาย" การส่งเป็นตัวดำเนินการอย่างชัดเจนที่ระบุการแปลง คุณหมายถึง " การแปลงตัวชี้โดยนัย"
Keith Thompson

11

ฉันจำได้ว่า Eric Lippert เคยกล่าวไว้ว่าการสืบทอดจากSystem.Objectชั้นเรียนนั้นทำให้ 'คุ้มค่าที่สุดสำหรับลูกค้า' [แก้ไข: ใช่เขาพูดมันที่นี่ ]:

... พวกเขาไม่ต้องการประเภทฐานทั่วไป ตัวเลือกนี้ไม่ได้ทำมาจากความจำเป็น มันทำมาจากความปรารถนาที่จะมอบคุณค่าที่ดีที่สุดให้กับลูกค้า

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

นั่นเป็นคำตอบที่คลุมเครือ หากคุณต้องการคำตอบที่เฉพาะเจาะจงยิ่งขึ้นลองถามคำถามที่เจาะจงกว่านี้

การมีทุกอย่างมาจากSystem.Objectชั้นเรียนนั้นให้ความน่าเชื่อถือและมีประโยชน์ที่ฉันเคารพมาก ฉันรู้ว่าวัตถุทั้งหมดจะมีประเภท ( GetType) ว่าพวกเขาจะสอดคล้องกับวิธีการสรุปของ CLR ( Finalize) และฉันสามารถใช้GetHashCodeกับพวกเขาเมื่อจัดการกับคอลเลกชัน


2
นี่คือข้อบกพร่อง คุณไม่ต้องการGetTypeคุณสามารถขยายtypeofเพื่อแทนที่การทำงานของมันได้ สำหรับในความเป็นFinalizeจริงแล้วหลายประเภทไม่สามารถสรุปได้ทั้งหมดและไม่มีวิธีการสรุปที่มีความหมาย นอกจากนี้ยังมีหลายประเภทที่ไม่แฮชและไม่สามารถแปลงเป็นสตริงได้ - โดยเฉพาะอย่างยิ่งประเภทภายในที่ไม่ได้มีไว้สำหรับสิ่งนั้นและไม่เคยถูกใช้เพื่อสาธารณะ ประเภททั้งหมดเหล่านี้มีส่วนต่อประสานที่ไม่จำเป็น
DeadMG

5
ไม่มีก็ไม่ได้มีข้อบกพร่อง - ฉันไม่เคยอ้างว่าคุณจะใช้GetType, FinalizeหรือสำหรับทุกGetHashCode System.Objectฉันแค่บอกว่าพวกเขาอยู่ที่นั่นถ้าคุณต้องการพวกเขา ในส่วนของ "อินเทอร์เฟซของพวกเขาไม่จำเป็นต้องปนเปื้อน" ฉันจะอ้างถึงคำกล่าวของ elippert ดั้งเดิมของฉัน: "ค่าที่ดีที่สุดสำหรับลูกค้า" เห็นได้ชัดว่าทีม. NET ยินดีที่จะจัดการกับการแลกเปลี่ยน
gws2

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

ฉันไม่ได้พยายามที่จะโต้แย้งจุดของคุณ @DeadMG - คำถามนี้มีเฉพาะ "ทำไมทุกคลาสใน. NET ทั่วโลกได้รับมรดกจาก Object Class" และฉันเชื่อมโยงไปยังโพสต์ก่อนหน้าโดยคนใกล้ชิดกับทีม. NET ที่ ไมโครซอฟท์ที่ให้สิทธิ์ถูกต้อง - แม้ว่าจะคลุมเครือ - ตอบว่าทำไมทีมพัฒนา. NET เลือกวิธีนี้
gws2

คลุมเครือเกินไปที่จะตอบคำถามนี้
DeadMG

4

ผมคิดว่า Java และ C # ออกแบบเพิ่มวัตถุรากเพราะมันเป็นฟรีเป็นหลักให้กับพวกเขาในขณะที่อาหารกลางวันฟรี

ค่าใช้จ่ายในการเพิ่มอ๊อบเจครูทเท่ากับค่าใช้จ่ายในการเพิ่มฟังก์ชั่นเสมือนแรกของคุณ เนื่องจากทั้ง CLR และ JVM เป็นสภาวะแวดล้อมที่รวบรวมขยะด้วย object finalizers คุณต้องมีเวอร์ชวลอย่างน้อยหนึ่งตัวสำหรับวิธีของคุณjava.lang.Object.finalizeหรือสำหรับSystem.Object.Finalizeวิธีการของคุณ ดังนั้นค่าใช้จ่ายในการเพิ่มวัตถุรูทของคุณจึงได้รับการชำระล่วงหน้าคุณสามารถได้รับผลประโยชน์ทั้งหมดโดยไม่ต้องเสียเงิน นี่คือสิ่งที่ดีที่สุดของทั้งสองโลก: ผู้ใช้ที่ต้องการคลาสรูททั่วไปจะได้รับสิ่งที่ต้องการและผู้ใช้ที่ไม่สนใจน้อยกว่าจะสามารถเขียนโปรแกรมได้ราวกับว่าไม่มี


4

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

ทำไม. NET จึงมีรูทร่วม

ในความหมายทางเทคนิคส่วนใหญ่การมีรากร่วมเป็นสิ่งจำเป็นสำหรับการไตร่ตรองและสำหรับภาชนะบรรจุพลศาสตร์ล่วงหน้า

นอกจากนี้สำหรับความรู้ของฉันมีรากร่วมกับวิธีการพื้นฐานเช่นequals()และhashCode()ได้รับในเชิงบวกมากใน Java และ C # ได้รับอิทธิพลจาก Java (ในหมู่อื่น ๆ ) Java ดังนั้นพวกเขาจึงต้องการที่จะมีคุณสมบัติเช่นกัน

อะไรคือข้อดีและข้อเสียของการมีรากร่วมกันในภาษา

ข้อดีของการมีรูทร่วม:

  • คุณสามารถอนุญาตให้ทุกวัตถุที่มีฟังก์ชั่นบางอย่างที่คุณพิจารณาที่สำคัญเช่นและequals() hashCode()ตัวอย่างเช่นนั่นหมายความว่าทุกวัตถุใน Java หรือ C # สามารถใช้ในแผนที่แฮช - เปรียบเทียบสถานการณ์นั้นกับ C ++
  • คุณสามารถอ้างถึงอ็อบเจกต์ที่ไม่ทราบประเภท - เช่นหากคุณเพิ่งถ่ายโอนข้อมูลไปทั่ว
  • คุณสามารถอ้างถึงวัตถุที่ไม่สามารถจำแนกได้เช่นเมื่อใช้การสะท้อน
  • มันสามารถใช้ในกรณีที่หายากเมื่อคุณต้องการที่จะสามารถยอมรับวัตถุที่อาจมีความแตกต่างและประเภทที่ไม่เกี่ยวข้อง - เช่นเป็นพารามิเตอร์ของprintfวิธีการเหมือน
  • มันช่วยให้คุณมีความยืดหยุ่นในการเปลี่ยนพฤติกรรมของวัตถุทั้งหมดโดยการเปลี่ยนเพียงชั้นเดียว ตัวอย่างเช่นถ้าคุณต้องการเพิ่มวิธีการทั้งหมดวัตถุในโปรแกรมของคุณ C # objectคุณสามารถเพิ่มวิธีขยายไป ไม่ธรรมดามากบางที แต่หากไม่มีรากร่วมมันก็เป็นไปไม่ได้

ข้อเสียของการมีรากที่พบบ่อย:

  • มันอาจถูกทารุณกรรมเพื่ออ้างถึงวัตถุที่มีค่าบางอย่างแม้ว่าคุณจะใช้ประเภทที่แม่นยำกว่านี้ได้ก็ตาม

คุณควรไปกับรูททั่วไปในโปรเจคเฟรมเวิร์กของคุณหรือไม่?

ส่วนตัวมากแน่นอน แต่เมื่อฉันมองไปที่ด้านบนของรายการโปร-Con ฉันแน่นอนตอบใช่ โดยเฉพาะกระสุนนัดสุดท้ายในรายการโปร - ความยืดหยุ่นในการเปลี่ยนพฤติกรรมของวัตถุทั้งหมดในภายหลังโดยการเปลี่ยนรูท - กลายเป็นประโยชน์อย่างมากในแพลตฟอร์มซึ่งการเปลี่ยนรูทนั้นเป็นที่ยอมรับของลูกค้ามากกว่าการเปลี่ยนแปลงทั้งหมด ภาษา.

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

แน่นอนว่าประเภทจะต้องมีความเกี่ยวข้องอย่างน้อยเล็กน้อยโดยเฉพาะอย่างยิ่งฉันไม่เคยเสียสละกฎ "is-a" เพียงเพื่อให้มีรากร่วมกัน


ฉันคิดว่า c # ไม่ได้รับอิทธิพลจาก Java เท่านั้นในบางประเด็น -was- Java Microsoft ไม่สามารถใช้ Java ได้อีกต่อไปดังนั้นจะทำให้เงินลงทุนหลายพันล้านเสียไป พวกเขาเริ่มด้วยการให้ภาษาที่พวกเขามีชื่อใหม่แล้ว
Mike Braun

3

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

หากคุณกำลังสร้างเฟรมเวิร์กสิ่งต่าง ๆ จะต้องถูกใช้โดย codebase ที่มีอยู่ คุณไม่สามารถมี supertype สากลได้เนื่องจากประเภทอื่น ๆ ทั้งหมดในสิ่งที่สิ้นเปลืองกรอบงานอยู่

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

แต่มันเกิดขึ้น ถ้าเป็นเช่นนั้นไปข้างหน้าในการสรุปพฤติกรรมที่พบบ่อย


2

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

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


2
นั่นไม่จริงเลย มีคลาสภายในเท่านั้นจำนวนมากซึ่งไม่เคยแฮชไม่เคยสะท้อนและไม่เคยแปลงเป็นสตริง มรดกที่ไม่จำเป็นและวิธีการฟุ่มเฟือยแน่นอนที่สุดไม่ละเมิดหลักการหลาย
DeadMG

2

เหตุผลสองประการที่ทำให้ฟังก์ชั่นทั่วไปทุกคลาสเด็กสามารถแชร์ได้ และยังให้ความสามารถในการเขียนเฟรมเวิร์กในการเขียนฟังก์ชันอื่น ๆ มากมายลงในเฟรมเวิร์กที่ทุกอย่างสามารถใช้ได้ ตัวอย่างจะเป็น ASP.NET แคและเซสชัน เกือบทุกอย่างสามารถเก็บไว้ในพวกเขาเพราะพวกเขาเขียนวิธีการเพิ่มเพื่อรับวัตถุ

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

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

หลายปีก่อนมีแอปพลิเคชันขนาดใหญ่มากที่ฉันสร้างคลาสรูต หลังจากไม่กี่เดือนของการพัฒนามันถูกบรรจุด้วยรหัสที่ไม่มีธุรกิจอยู่ในนั้น เรากำลังแปลงแอปพลิเคชัน ASP เก่าและคลาสรูตเป็นการแทนที่ไฟล์ global.inc เก่าที่เราเคยใช้ในอดีต ต้องเรียนรู้บทเรียนนั้นอย่างหนัก


2

วัตถุฮีปแบบสแตนด์อโลนทั้งหมดสืบทอดมาจากObject; ที่เหมาะสมเนื่องจากวัตถุฮีปแบบสแตนด์อโลนทั้งหมดต้องมีลักษณะทั่วไปบางอย่างเช่นวิธีการระบุประเภท ไม่เช่นนั้นหากตัวเก็บขยะมีการอ้างอิงถึงวัตถุฮีปที่ไม่ทราบประเภทมันจะไม่มีทางรู้ว่าบิตใดในหน่วยความจำที่เกี่ยวข้องกับวัตถุนั้นควรถือเป็นการอ้างอิงไปยังวัตถุฮีปอื่น

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

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

ทั้งหมดนี้มีความสำคัญเนื่องจากการอ้างอิงไปยังวัตถุฮีปชนิดค่าทำหน้าที่เหมือนกับการอ้างอิงคลาสและไม่เหมือนกับประเภทค่า พิจารณาตัวอย่างรหัสต่อไปนี้:

สตริง testEnumerator <T> (T) โดยที่ T: IEnumerator <string>
{
  var it2 = มัน;
  it.MoveNext ();
  it2.MoveNext ();
  กลับมาในปัจจุบัน;
}
การทดสอบเป็นโมฆะสาธารณะ ()
{
  var theList = รายการใหม่ <string> ();
  theList.Add ( "เฟร็ด");
  theList.Add ( "จอร์จ");
  theList.Add ( "เพอร์ซี่");
  theList.Add ( "มอลลี่");
  theList.Add ( "รอน");

  var enum1 = theList.GetEnumerator ();
  IEnumerator <string> enum2 = enum1;

  Debug.Print (testEnumerator (enum1));
  Debug.Print (testEnumerator (enum1));
  Debug.Print (testEnumerator (enum2));
  Debug.Print (testEnumerator (enum2));
}

หากtestEnumerator()วิธีการผ่านสถานที่เก็บของประเภทค่าitจะได้รับอินสแตนซ์ที่มีการคัดลอกฟิลด์สาธารณะและส่วนตัวจากค่าการส่งผ่าน ตัวแปรท้องถิ่นจะถือกรณีอื่นที่มีเขตข้อมูลทั้งหมดจะถูกคัดลอกจากit2 itการโทรMoveNextเข้าit2จะไม่ส่งผลกระทบitใด ๆ

หากโค้ดด้านบนถูกส่งผ่านที่เก็บข้อมูลของประเภทคลาสดังนั้นค่าการส่งผ่านitและและit2ทั้งหมดจะอ้างถึงวัตถุเดียวกันดังนั้นการเรียกใช้MoveNext()ที่ใดก็ได้จะเรียกมันได้อย่างมีประสิทธิภาพ

โปรดทราบว่าการส่งList<String>.Enumeratorเพื่อIEnumerator<String>เปลี่ยนได้อย่างมีประสิทธิภาพจากประเภทค่าเป็นประเภทคลาส ชนิดของวัตถุฮีปคือList<String>.Enumeratorแต่พฤติกรรมจะแตกต่างจากประเภทค่าของชื่อเดียวกัน


+1 สำหรับการไม่พูดถึง ToString ()
JeffO

1
นี่คือทั้งหมดที่เกี่ยวกับรายละเอียดการใช้งาน ไม่จำเป็นต้องเปิดเผยสิ่งเหล่านี้กับผู้ใช้
DeadMG

@DeadMG: คุณหมายถึงอะไร พฤติกรรมที่สังเกตได้ของสิ่งList<T>.Enumeratorซึ่งถูกเก็บไว้ใน a List<T>.Enumeratorนั้นแตกต่างอย่างมากจากพฤติกรรมของสิ่งหนึ่งซึ่งถูกเก็บไว้ในIEnumerator<T>ที่ซึ่งการจัดเก็บค่าของชนิดเก่าไปยังที่เก็บข้อมูลไม่ว่าชนิดใดจะทำสำเนาสถานะของตัวแจงนับ ในขณะที่การจัดเก็บค่าประเภทหลังไปยังที่เก็บข้อมูลซึ่งเป็นประเภทหลังจะไม่ทำสำเนา
supercat

ใช่ แต่Objectมีอะไรจะทำอย่างไรกับความจริงที่ว่า
DeadMG

@DeadMG: จุดของฉันคืออินสแตนซ์ของประเภทฮีปSystem.Int32ยังเป็นอินสแตนซ์ของSystem.Objectแต่ที่เก็บข้อมูลของประเภทSystem.Int32เก็บทั้งSystem.Objectอินสแตนซ์หรือการอ้างอิงถึงหนึ่ง
supercat

2

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

การมีลำดับชั้นเดียวกับObject(หรือบางอย่างที่คล้ายกัน) ที่รูททำให้มันค่อนข้างง่าย (ตัวอย่างหนึ่ง) ในการสร้างคลาสคอลเลกชันของคุณเป็นคอลเล็กชั่Objectนดังนั้นจึงเป็นเรื่องง่ายที่คอลเลกชันจะมีวัตถุประเภทใด ๆ

เพื่อเป็นการตอบแทนความได้เปรียบเล็กน้อยคุณจะได้รับข้อเสียทั้งหมด ก่อนอื่นจากมุมมองการออกแบบคุณจะต้องจบด้วยความคิดบ้า ๆ บอ ๆ อย่างน้อยตามมุมมอง Java ของจักรวาลความเชื่อว่าไม่มีพระเจ้าและป่ามีอะไรเหมือนกัน? ว่าพวกเขาทั้งสองมี hashcodes! แผนที่คือชุดสะสมหรือไม่? ตามที่ชวาไม่มีมันไม่ใช่!

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

เมื่อ Java (และระดับที่น้อยกว่า,. NET) ได้รับการออกแบบลำดับชั้นของเสาหินอาจถูกมองว่าเป็นตัวเลือก "ปลอดภัย" - หนึ่งที่มีปัญหา แต่เป็นปัญหาที่รู้จักกันเป็นส่วนใหญ่ ในทางกลับกันการเขียนโปรแกรมทั่วไปเป็นสิ่งที่เกือบทุกคนตระหนักถึงแล้ว แต่อย่างน้อยก็เป็นวิธีการที่ดีกว่าในทางทฤษฎีแต่สิ่งหนึ่งที่ผู้พัฒนาเชิงพาณิชย์จำนวนมากมองว่าเป็นการสำรวจที่ค่อนข้างแย่และ / หรือมีความเสี่ยง , Ada ถูกไล่ออกอย่างมากเนื่องจากความล้มเหลว)

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

สำหรับการออกแบบใหม่ในวันนี้ไม่มีคำถามที่สมเหตุสมผล: การใช้ลำดับชั้นแบบเสาหินเป็นความผิดพลาดที่ชัดเจนและเป็นความคิดที่ไม่ดี


เป็นค่าที่ระบุว่าเทมเพลตนั้นน่ากลัวและมีประโยชน์ก่อนที่จะส่ง. NET
DeadMG

ในโลกการค้า, Ada เป็นความล้มเหลวที่ไม่มากเนื่องจากมันฟุ่มเฟื่อย แต่เนื่องจากการขาดการเชื่อมต่อมาตรฐานเพื่อการดำเนินงานการให้บริการระบบ ไม่มี API มาตรฐานสำหรับระบบไฟล์กราฟิกเครือข่าย ฯลฯ ทำให้การพัฒนา Ada ของแอปพลิเคชั่น 'โฮสต์' มีราคาแพงมาก
วินไคลน์

@ kevincline: ฉันน่าจะพูดด้วยคำที่แตกต่างออกไปเล็กน้อย - ถึงผลกระทบที่ Ada โดยรวมถูกไล่ออกเนื่องจากความล้มเหลวเนื่องจากความล้มเหลวในตลาด การปฏิเสธที่จะใช้ Ada คือ (IMO) ค่อนข้างสมเหตุสมผล - แต่ปฏิเสธที่จะเรียนรู้จากมันน้อยมาก (อย่างดีที่สุด)
Jerry Coffin

@Jerry: ฉันคิดว่าเทมเพลต C ++ นำแนวคิดของ Ada generics ไปใช้แล้วปรับปรุงพวกเขาอย่างมากด้วยการสร้างอินสแตนซ์โดยนัย
วินไคลน์

1

สิ่งที่คุณต้องการทำนั้นน่าสนใจจริง ๆ มันยากที่จะพิสูจน์ว่ามันผิดหรือถูกจนกว่าคุณจะทำเสร็จ

นี่คือสิ่งที่คิดเกี่ยวกับ:

  • เมื่อใดที่คุณจะตั้งใจส่งผ่านวัตถุระดับบนสุดนี้ไปยังวัตถุที่เฉพาะเจาะจงมากกว่านี้
  • คุณตั้งใจจะใส่รหัสอะไรลงในทุก ๆ คลาสของคุณ?

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

นอกจากนี้จากประสบการณ์ส่วนตัว:

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

นั่นเป็นห้องสมุดที่ใช้งานยากที่สุดที่ฉันเคยจัดการมา


0

เพราะนั่นคือหนทางของ OOP สิ่งสำคัญคือต้องมีประเภท (วัตถุ) ที่สามารถอ้างถึงอะไรก็ได้ ข้อโต้แย้งของวัตถุประเภทสามารถยอมรับอะไรก็ได้ นอกจากนี้ยังมีการสืบทอดคุณสามารถมั่นใจได้ว่า ToString (), GetHashCode () และอื่น ๆ สามารถใช้ได้กับทุกสิ่ง

แม้แต่ออราเคิลก็ได้ตระหนักถึงความสำคัญของการมีประเภทฐานและกำลังวางแผนที่จะลบการใช้งานดั้งเดิมใน Java ใกล้ปี 2017


6
มันไม่ใช่หนทางของ OOP หรือสำคัญ คุณไม่ได้ให้ข้อโต้แย้งที่แท้จริงนอกจาก "ดี"
DeadMG

1
@DeadMG คุณสามารถอ่านข้อโต้แย้งที่ผ่านประโยคแรกได้ ดึกดำบรรพ์พฤติกรรมแตกต่างจากวัตถุจึงบังคับให้โปรแกรมเมอร์ที่จะเขียนหลายตัวแปรของรหัสวัตถุประสงค์เดียวกันสำหรับวัตถุและดั้งเดิม
Dante

2
@DeadMG ดีเขาบอกว่ามันหมายความว่าคุณสามารถคาดเดาToString()และGetType()จะอยู่ในทุกวัตถุ มันอาจคุ้มค่าที่จะชี้ให้เห็นว่าคุณสามารถใช้ตัวแปรชนิดหนึ่งobjectเพื่อเก็บออบเจ็กต์. NET
JohnL

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

คุณควรมีวัตถุในระดับที่มีรหัสเฉพาะคุณไม่ควรมีเพียงแค่กราฟที่ผูกเข้าด้วยกัน "วัตถุ" จริง ๆ แล้วใช้วิธีการที่สำคัญบางอย่างและทำหน้าที่เป็นวิธีในการส่งวัตถุไปยังเครื่องมือที่ประเภทยังไม่ได้สร้างขึ้น
Bill K
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.