ทำไมอาร์เรย์จึงใช้ IList


141

ดูคำจำกัดความของคลาสSystem.Array

public abstract class Array : IList, ...

ในทางทฤษฎีฉันควรจะสามารถเขียนบิตนี้และมีความสุข

int[] list = new int[] {};
IList iList = (IList)list;

ฉันควรจะสามารถเรียกวิธีการใด ๆ จาก iList

 ilist.Add(1); //exception here

คำถามของฉันไม่ใช่ว่าทำไมฉันถึงได้รับการยกเว้น แต่ทำไม Array จึงใช้ IList ?


22
เป็นคำถามที่ดี ฉันไม่เคยชอบไอเดียของอินเทอร์เฟซไขมัน (นั่นเป็นศัพท์เทคนิคสำหรับการออกแบบประเภทนี้)
Konrad Rudolph


2
ใครบ้างที่สนใจเกี่ยวกับ LSP ดูเหมือนว่าเป็นเรื่องวิชาการสำหรับฉัน
Gabe

13
@Gabe แล้วคุณต้องทำงานกับฐานโค้ดขนาดใหญ่ การนำพฤติกรรมไปใช้ (สืบทอดมาจากส่วนต่อประสาน) จากนั้นก็ไม่สนใจสิ่งที่คุณไม่ชอบ / ไม่สามารถสนับสนุนได้ซึ่งจะนำไปสู่การส่งกลิ่นทำให้งงงวยหล่อและในที่สุด: รหัสรถ
Marius

3
@Gabe คอลเลกชันซึ่งหมายถึงความไม่แน่นอนของหน่วยงานที่มีอยู่ คุณสามารถทำให้สมาชิกคลาสของคุณเป็นชนิดที่ใช้ทั้ง IRWList <> และ IReadList <>, ใช้ถ้าเป็น IRWList <> ภายในภายในคลาสของคุณและแสดงเป็น IReadList ใช่คุณต้องใส่ความซับซ้อนไว้ที่ไหนสักแห่ง แต่ฉันไม่เห็นว่ามันจะนำไปใช้กับการมองข้าม LSP ได้อย่างไรในฐานะหลักการออกแบบที่ดีมาก (ไม่รู้เรื่องคุณสมบัติ IsReadOnly แม้ว่าจะทำให้ IList ซับซ้อนมากขึ้นจากมุมมองของผู้บริโภค)
Marius

คำตอบ:


94

เนื่องจากอาเรย์ช่วยให้เข้าถึงดัชนีได้อย่างรวดเร็วและIList/ IList<T>เป็นอินเทอร์เฟซการรวบรวมที่สนับสนุนสิ่งนี้เท่านั้น ดังนั้นคำถามที่แท้จริงของคุณคือ "ทำไมไม่มีอินเทอร์เฟซสำหรับคอลเลกชันคงที่กับตัวจัดทำดัชนี" และเพื่อที่ฉันไม่มีคำตอบ

ไม่มีอินเทอร์เฟซแบบอ่านอย่างเดียวสำหรับคอลเลกชัน และฉันคิดถึงสิ่งเหล่านั้นมากกว่าขนาดคงที่ด้วยส่วนต่อประสานดัชนี

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

  • เพียงแค่แจง IEnumerable<T>
  • อ่านอย่างเดียว แต่ไม่มีตัวทำดัชนี (.Count, .Contains, ... )
  • ปรับขนาดได้ แต่ไม่มีตัวทำดัชนีเช่นตั้งค่าเป็น (เพิ่มลบ ... ) ปัจจุบัน ICollection<T>
  • อ่านอย่างเดียวพร้อมตัวจัดทำดัชนี (ตัวจัดทำดัชนี, ดัชนีของ ... )
  • ขนาดคงที่พร้อมตัวทำดัชนี (ตัวทำดัชนีพร้อมตัวตั้ง)
  • ขนาดตัวแปรพร้อมตัวทำดัชนี (แทรก, ... ) ปัจจุบัน IList<T>

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


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

16
มันทำลาย LSP ถ้าไม่มีรายการเพิ่ม (รายการ) ควรเพิ่มรายการลงในรายการโดยไม่คำนึงถึงประเภทที่เป็นรูปธรรม ยกเว้นสำหรับกรณี Exel ในการดำเนินการในอาร์เรย์พ่นยกเว้นในกรณีที่ไม่ใช่ exceptionel ซึ่งในนั้นด้วยตนเองคือการปฏิบัติที่ไม่ดี
Rune FS

2
@smelch ฉันขอโทษ แต่คุณได้รับ LSP ผิดแล้ว อาร์เรย์ไม่ได้ใช้งานaddและไม่สามารถทดแทนสิ่งที่ทำเมื่อต้องการความสามารถนั้น
Rune FS

7
ผมยอมรับว่ามันได้ในทางเทคนิคไม่ละเมิด LSP เพียงเพราะเอกสารระบุคุณควรตรวจสอบIsFixedSizeและIsReadOnlyคุณสมบัติมันแน่นอนละเมิดบอกไม่ขอหลักการและหลักการของความประหลาดใจไม่น้อยกว่า เหตุใดจึงต้องใช้อินเทอร์เฟซเมื่อคุณเพิ่งจะโยนข้อยกเว้นสำหรับ 4 จาก 9 วิธี?
Matthew

11
เวลาผ่านไปนับตั้งแต่คำถามเดิม แต่ตอนนี้กับสุทธิ 4.5 มีอินเตอร์เฟซเพิ่มเติมIReadOnlyListและIReadOnlyCollection
Tobias

43

ส่วนข้อสังเกตของเอกสารสำหรับIListพูดว่า

IList เป็นผู้สืบทอดของอินเตอร์เฟส ICollection และเป็นอินเตอร์เฟสพื้นฐานของรายการที่ไม่ใช่ทั่วไปทั้งหมด การใช้งาน IList ตกอยู่ในสามประเภท: อ่านอย่างเดียวขนาดคงที่และตัวแปรขนาด ไม่สามารถแก้ไข IList แบบอ่านอย่างเดียว IList ขนาดคงที่ไม่อนุญาตให้มีการเพิ่มหรือลบองค์ประกอบ แต่อนุญาตการแก้ไของค์ประกอบที่มีอยู่ IList ขนาดผันแปรอนุญาตให้มีการเพิ่มการลบและการแก้ไของค์ประกอบ

เห็นได้ชัดว่าอาร์เรย์ตกอยู่ในหมวดหมู่ขนาดคงที่ดังนั้นโดยการขาดของอินเตอร์เฟซมันทำให้รู้สึก


4
ฉันเดาว่าพวกเขาจะมีอินเตอร์เฟสมากมาย IListFixedSize, IListReadOnly ...
แมกนัส

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

1
@oleksii: ฉันเห็นด้วย การเชื่อมต่อและข้อยกเว้นรันไทม์ไม่ใช่ชุดค่าผสมที่หรูหราที่สุด ในการป้องกันของArrayมันจะใช้Addวิธีการอย่างชัดเจนซึ่งจะช่วยลดความเสี่ยงของการเรียกมันโดยไม่ได้ตั้งใจ
Brian Rasmussen

จนกว่าเราจะสร้างการดำเนินการตามIListที่ไม่อนุญาตทั้งการแก้ไขและการเพิ่ม / ลบ จากนั้นเอกสารจะไม่ถูกต้องอีกต่อไป : P
Timo

1
@Magnus - ในสุทธิ 4.5 มีอินเตอร์เฟซเพิ่มเติมIReadOnlyListและIReadOnlyCollection
RBT

17

เนื่องจากไม่ใช่ILists ทั้งหมดที่ไม่แน่นอน (ดูIList.IsFixedSizeและIList.IsReadOnly) และอาร์เรย์ทำหน้าที่เหมือนรายการขนาดคงที่

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


10
@oleksii: ไม่มันไม่ทำลาย LSP เพราะIList ตัวอินเตอร์เฟสเองบอกคุณว่ามันไม่สามารถเปลี่ยนแปลงได้ หากในความเป็นจริงรับประกันว่าจะไม่แน่นอนและอาร์เรย์บอกคุณเป็นอย่างอื่นแล้วมันจะทำลายกฎ
user541686

ที่จริงแล้ว Array จะแบ่ง LSP ในกรณีที่เป็นแบบทั่วไปIList<T>และไม่แตกในกรณีที่ไม่ใช่แบบทั่วไปIList: enterprisecraftsmanship.com/2014/11/22/…
Vladimir

5

มันเป็นมรดกที่เรามีมาตั้งแต่สมัยที่มันยังไม่ชัดเจนว่าจะจัดการกับคอลเลกชันแบบอ่านอย่างเดียวได้อย่างไรและอาเรย์อ่านได้อย่างเดียวหรือไม่ มีแฟล็ก IsFixedSize และ IsReadOnly ในอินเตอร์เฟส IList IsReadOnly ตั้งค่าสถานะหมายความว่าคอลเลกชันไม่สามารถเปลี่ยนแปลงได้เลยและ IsFixedSize หมายความว่าคอลเลกชันไม่อนุญาตให้มีการปรับเปลี่ยน แต่ไม่เพิ่มหรือลบรายการ

ในช่วงเวลาของสุทธิ 4.5 มันเป็นที่ชัดเจนว่าบางอินเตอร์เฟซ "กลาง" จะต้องทำงานร่วมกับคอลเลกชันอ่านอย่างเดียวดังนั้นIReadOnlyCollection<T>และIReadOnlyList<T>ถูกนำมาใช้

นี่คือบล็อกโพสต์ที่ยอดเยี่ยมที่อธิบายรายละเอียด: อ่านคอลเล็กชันใน. NET เท่านั้น


0

คำจำกัดความของอินเทอร์เฟซ IList คือ "แสดงถึงการรวบรวมวัตถุที่ไม่ใช่แบบทั่วไปที่สามารถเข้าถึงได้โดยดัชนี" Array เป็นไปตามข้อกำหนดนี้อย่างสมบูรณ์ดังนั้นจึงต้องนำอินเตอร์เฟส ข้อยกเว้นเมื่อเรียกใช้เมธอด Add () คือ "System.NotSupportedException: การรวบรวมมีขนาดคงที่" และเกิดขึ้นเนื่องจากอาร์เรย์ไม่สามารถเพิ่มความจุได้แบบไดนามิก กำลังการผลิตถูกกำหนดในระหว่างการสร้างวัตถุอาร์เรย์


0

การใช้อาเรย์ใช้ IList (และทรานซิทชัน, ICollection) ทำให้เครื่องยนต์ Linq2Objects ใช้งานได้ง่ายขึ้นเนื่องจากการส่ง IEnumerable ไปยัง IList / ICollection ก็จะทำงานได้ดีขึ้นเช่นกัน

ตัวอย่างเช่นการนับ () จบลงด้วยการเรียก Array.Length ภายใต้ประทุนเนื่องจากมันถูกส่งไปยัง ICollection และการนำไปใช้ของอาร์เรย์จะคืนค่าความยาว

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

นั่นคือสิ่งที่ฉันใช้ใน "ทำไม"


0

นอกจากนี้ยังมีรายละเอียดการใช้งาน LINQ การตรวจสอบล่าสุดสำหรับ IList หากไม่ได้ใช้รายการพวกเขาจะต้องใช้ 2 การตรวจสอบการชะลอตัวของการโทรครั้งสุดท้ายทั้งหมด

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