รายการ <T> หรือ IList <T> [ปิด]


495

ใครสามารถอธิบายให้ฉันทำไมฉันต้องการใช้ IList มากกว่ารายชื่อใน C #?

คำถามที่เกี่ยวข้อง: เหตุใดจึงถือว่าไม่ดีที่จะเปิดเผยList<T>


1
ค้นหา "รหัสไปยังอินเทอร์เฟซไม่ใช่การใช้งานอินเทอร์เน็ต" และคุณสามารถอ่านได้ทั้งวัน
granadaCoder

คำตอบ:


446

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

หากคุณเพียงแค่ใช้มันภายในคุณอาจไม่สนใจมากนักและการใช้List<T>อาจจะไม่เป็นไร


19
ฉันไม่เข้าใจความแตกต่าง (บอบบาง) มีตัวอย่างที่คุณสามารถชี้ให้ฉันได้หรือไม่
StingyJack

134
สมมติว่าคุณเคยใช้ List <T> และต้องการเปลี่ยนเพื่อใช้ CaseInsensitiveList พิเศษ <T> ซึ่งทั้งคู่ใช้ IList <T> หากคุณใช้ประเภทที่เป็นรูปธรรมผู้โทรทุกคนจะต้องมีการปรับปรุง หากเปิดเผยเป็น IList <T> ผู้โทรไม่จำเป็นต้องเปลี่ยน
tvanfosson

46
อา. ตกลง. ฉันเข้าใจ. การเลือกตัวหารร่วมที่ต่ำที่สุดสำหรับสัญญา ขอบคุณ!
StingyJack

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

14
โปรดทราบว่า T []: IList <T> เพื่อเหตุผลด้านประสิทธิภาพคุณสามารถสลับจากการส่ง List <T> จากกระบวนงานของคุณกลับคืนเป็น T [] ได้ตลอดเวลาและหากขั้นตอนนั้นถูกประกาศให้ส่งคืน IList <T> (หรืออาจ ICollection <T> หรือ IEnumerable <T>) ไม่มีอะไรที่จะต้องเปลี่ยนอีก
yfeldblum

322

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

นักบินอวกาศสถาปัตยกรรม โอกาสที่คุณจะเคยเขียน IList ของคุณเองที่เพิ่มอะไรลงไปในสิ่งที่อยู่ในกรอบการทำงาน. NET นั้นห่างไกลมากจนมันหมองคล้ำทางทฤษฎีที่สงวนไว้สำหรับ

นักบินอวกาศซอฟต์แวร์

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


65
ฉันไม่เห็นด้วยกับคำตอบที่ไร้สาระของคุณ ฉันไม่เห็นด้วยกับความรู้สึกของสถาปัตยกรรมที่เกินจริงเป็นปัญหาที่แท้จริง อย่างไรก็ตามฉันคิดว่าโดยเฉพาะอย่างยิ่งในกรณีของคอลเลกชันที่อินเตอร์เฟสส่องแสงจริงๆ สมมติว่าฉันมีฟังก์ชั่นที่ส่งคืนIEnumerable<string>ภายในฟังก์ชันที่ฉันอาจใช้List<string>สำหรับที่เก็บข้อมูลสำรองภายในเพื่อสร้างคอลเลกชันของฉัน แต่ฉันต้องการให้ผู้โทรระบุเนื้อหาของมันเท่านั้นไม่ใช่เพิ่มหรือลบ การยอมรับอินเทอร์เฟซเป็นพารามิเตอร์สื่อสารข้อความที่คล้ายกัน "ฉันต้องการชุดของสตริงไม่ต้องกังวลว่าฉันจะไม่เปลี่ยน"
joshperry

38
การพัฒนาซอฟต์แวร์นั้นเกี่ยวกับการแปลเจตนา หากคุณคิดว่าอินเทอร์เฟซมีประโยชน์สำหรับการสร้างสถาปัตยกรรมที่ยิ่งใหญ่และไม่มีสถานที่ในร้านค้าเล็ก ๆ ฉันหวังว่าคนที่นั่งตรงข้ามคุณในการสัมภาษณ์ไม่ใช่ฉัน
joshperry

48
บางคนต้องพูดว่า Arec นี้ ... ถึงแม้ว่าคุณจะมีรูปแบบการศึกษาไล่จดหมายเกลียดทุกครั้ง +1 สำหรับพวกเราทุกคนที่เกลียดชังเมื่อมีแอปขนาดเล็กที่เต็มไปด้วยอินเทอร์เฟซและคลิกที่ "ค้นหาคำจำกัดความ" พาเราไปที่อื่นที่มาของปัญหา ... ฉันจะขอยืมวลี "Architecture Astronauts" ได้หรือไม่ ฉันเห็นว่ามันจะมีประโยชน์
Gats

6
ถ้าเป็นไปได้ฉันจะให้ 1,000 คะแนนสำหรับคำตอบนี้ : D
ซามูเอล

8
ฉันกำลังพูดถึงผลลัพธ์ที่กว้างขึ้นของการไล่ตามลวดลาย อินเทอร์เฟซสำหรับอินเทอร์เฟซทั่วไปดีเลเยอร์พิเศษเพื่อไล่รูปแบบไม่ดี
Gats

186

อินเทอร์เฟซคือสัญญา (หรือสัญญา)

มันเป็นเสมอกับสัญญา - ขนาดเล็กที่ดีกว่า


56
ไม่ดีกว่าเสมอไปรักษาง่ายกว่า! :)
Kirk Broadhurst

5
ฉันไม่เข้าใจสิ่งนี้ ดังนั้นIListสัญญาคือ สัญญาที่เล็กกว่าและดีกว่าที่นี่คืออะไร? นี่ไม่ใช่เหตุผล
nawfal

3
สำหรับชั้นเรียนอื่นฉันจะเห็นด้วย แต่ไม่ใช่สำหรับList<T>เนื่องจากAddRangeไม่ได้กำหนดไว้ในส่วนต่อประสาน
Jesse de Wit

3
นี่ไม่เป็นประโยชน์จริง ๆ ในกรณีนี้โดยเฉพาะ สัญญาที่ดีที่สุดคือสัญญาที่ถูกเก็บไว้ IList<T>ทำให้สัญญาไม่สามารถรักษาได้ ตัวอย่างเช่นมีAddวิธีที่เกินกว่าที่จะโยนได้หากIList<T>เกิดขึ้นเป็นแบบอ่านอย่างเดียว List<T>ในทางกลับกันเขียนได้เสมอและรักษาสัญญาไว้เสมอ นอกจากนี้ตั้งแต่ .NET 4.6 ขณะนี้เรามีIReadOnlyCollection<T>และที่มักจะพอดีกับที่ดีกว่าIReadOnlyList<T> IList<T>และพวกเขาเป็นโควาเรียนท์ หลีกเลี่ยงIList<T>!
ZunTzu

@ ZunTzu ฉันขอเถียงว่ามีใครบางคนผ่านคอลเล็กชั่นที่ไม่เปลี่ยนรูปแบบในฐานะที่ไม่แน่นอนได้ทำลายสัญญาที่นำเสนออย่างรู้ตัว - มันไม่ใช่ปัญหาของสัญญาเช่นIList<T>กัน บางคนก็สามารถนำไปใช้IReadOnlyList<T>และทำให้ทุกวิธีมีข้อยกเว้นเช่นกัน มันตรงกันข้ามกับการพิมพ์เป็ด - คุณเรียกมันว่าเป็ด แต่มันไม่สามารถต้มตุ๋นเหมือนอย่างใดอย่างหนึ่งได้ นั่นเป็นส่วนหนึ่งของสัญญาแม้ว่าคอมไพเลอร์จะไม่สามารถบังคับใช้ได้ ฉันยอมรับ 100% ว่าIReadOnlyList<T>หรือIReadOnlyCollection<T>ควรใช้เพื่อการเข้าถึงแบบอ่านอย่างเดียว
Shelby Oldfield

93

บางคนพูดว่า "ใช้IList<T>แทนทุกครั้งList<T>"
พวกเขาต้องการให้คุณสามารถเปลี่ยนลายเซ็นของคุณจากวิธีการที่จะvoid Foo(List<T> input)void Foo(IList<T> input)

คนเหล่านี้ผิด

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

ในฐานะที่เป็นโต้เถียงเล็กน้อยคุณอาจพบว่าผู้ที่โทรเข้ามาทุกคนต้องได้รับList<T>และรหัสการโทรก็เกลื่อนไปด้วย.ToList()

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

สมมติว่าคุณมีวิธีนี้:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

เพื่อนร่วมงานที่เป็นประโยชน์ "refactors" IList<int>วิธีการที่จะยอมรับ

ขณะนี้รหัสของคุณint[]ใช้งานไม่ได้เนื่องจากใช้งานIList<int>แต่มีขนาดคงที่ สัญญาสำหรับICollection<T>(ฐานIList<T>) ต้องใช้รหัสที่ใช้ตรวจสอบการIsReadOnlyตั้งค่าสถานะก่อนพยายามเพิ่มหรือลบรายการออกจากคอลเลกชัน สัญญาสำหรับList<T>ไม่

หลักการชดเชย Liskov (ประยุกต์) ระบุว่าประเภทที่ได้รับควรจะสามารถใช้แทนประเภทฐานโดยไม่มีเงื่อนไขเพิ่มเติมหรือ postconditions

มันให้ความรู้สึกเหมือนว่ามันทำลายหลักการแทนที่ Liskov

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

แต่มันก็ไม่ได้ คำตอบนี้คือตัวอย่างที่ใช้ IList <T> / ICollection <T> ผิด หากคุณใช้ ICollection <T> คุณต้องตรวจสอบการตั้งค่าสถานะ IsReadOnly

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

หากมีคนส่ง Array หรือ List ให้คุณรหัสของคุณจะทำงานได้ดีถ้าคุณตรวจสอบการตั้งค่าสถานะทุกครั้งและมี fallback ... แต่จริงๆแล้ว; ใครทำอย่างนั้น? คุณไม่ทราบล่วงหน้าหากวิธีการของคุณต้องการรายชื่อที่สามารถรับสมาชิกเพิ่มเติมได้ คุณไม่ได้ระบุไว้ในลายเซ็นวิธี? ว่าสิ่งที่คุณกำลังจะทำอะไรถ้าคุณได้ผ่านการอ่านรายชื่อเฉพาะเช่นint[]?

คุณสามารถใช้แทนList<T>เป็นรหัสที่ใช้IList<T>/ ได้อย่างถูกต้องICollection<T> คุณไม่สามารถรับประกันได้ว่าคุณสามารถทดแทนIList<T>/ เป็นรหัสที่ใช้ICollection<T>List<T>

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

สำหรับฉันIList<T>(และICollection<T>) เป็นส่วนที่แย่ที่สุดของ. NET Framework IsReadOnlyละเมิดหลักการของความประหลาดใจน้อยที่สุด คลาสเช่นArrayที่ไม่อนุญาตให้เพิ่มการแทรกหรือลบรายการไม่ควรใช้อินเทอร์เฟซด้วยวิธีการเพิ่มแทรกและเอาออก (โปรดดู/software/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties )

เป็นIList<T>แบบที่ดีสำหรับองค์กรของคุณ? ถ้าเพื่อนร่วมงานขอให้คุณเปลี่ยนลายเซ็นวิธีที่จะใช้IList<T>แทนการถามพวกเขาว่าพวกเขาต้องการเพิ่มองค์ประกอบต่อไปยังList<T> IList<T>หากพวกเขาไม่ทราบเกี่ยวกับIsReadOnly(และคนส่วนใหญ่ทำไม่ได้) IList<T>แล้วไม่ได้ใช้ เคย


โปรดทราบว่าการตั้งค่าสถานะ IsReadOnly มาจาก ICollection <T> และระบุว่ารายการสามารถเพิ่มหรือลบออกจากคอลเลกชัน; แต่เพียงเพื่อสร้างความสับสนให้กับสิ่งต่าง ๆ จริง ๆ มันไม่ได้ระบุว่าสามารถแทนที่ได้หรือไม่ซึ่งในกรณีของอาร์เรย์ (ซึ่งคืน IsReadOnlys == จริง) สามารถ

ดูข้อมูลเพิ่มเติมเกี่ยวกับ IsReadOnly ได้ที่คำจำกัดความ msdn ของ ICollection <T> IsReadOnly


5
ยอดเยี่ยมคำตอบที่ดี ฉันต้องการเพิ่มสิ่งนั้นแม้ว่าIsReadOnlyจะtrueเป็นIListคุณก็ยังสามารถแก้ไขสมาชิกปัจจุบันได้! บางทีคุณสมบัตินั้นควรตั้งชื่อIsFixedSize?
xofz

(ที่ได้รับการทดสอบด้วยการส่งผ่านArrayเป็นIListเพื่อที่ว่าทำไมฉันสามารถปรับเปลี่ยนสมาชิกในปัจจุบัน)
xofz

1
@SamPearson มีคุณสมบัติ IsFixedSize บน IList ใน. NET 1 ซึ่งไม่รวมอยู่ใน IList ทั่วไปของ. NET 2.0 <T> ซึ่งมันถูกรวมเข้ากับ IsReadOnly ทำให้เกิดพฤติกรรมที่น่าประหลาดใจในอาร์เรย์ มีบล็อกการหลอมละลายสมองอย่างละเอียดเกี่ยวกับความแตกต่างเล็ก ๆ น้อย ๆ ที่นี่: enterprisecraftsmanship.com/2014/11/22/…
ความสมบูรณ์แบบ

7
คำตอบที่ยอดเยี่ยม! นอกจากนี้ตั้งแต่ .NET 4.6 ขณะนี้เรามีIReadOnlyCollection<T>และIReadOnlyList<T>ที่เกือบจะดีกว่าเสมอพอดีกว่าแต่ไม่ได้มีความหมายที่เป็นอันตรายของขี้เกียจIList<T> IEnumerable<T>และพวกเขาเป็นโควาเรียนท์ หลีกเลี่ยงIList<T>!
ZunTzu

37

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

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

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

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


เกี่ยวกับคำสั่งสุดท้ายของคุณเกี่ยวกับการใช้ IEnumerable แทน IList ไม่แนะนำเสมอใช้ WPF เช่นซึ่งเพิ่งจะสร้าง wrapper IList object ดังนั้นจะมีผลกระทบของ perf - msdn.microsoft.com/en-us/library/bb613546.aspx
HAdes

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

1
@joshperry หากประสิทธิภาพของอินเทอร์เฟซรบกวนคุณอยู่ในแพลตฟอร์มที่ไม่ถูกต้องตั้งแต่แรก Impo นี่คือ microoptimization
nawfal

@nawfal ฉันไม่ได้แสดงความคิดเห็นกับข้อดีข้อเสียของอินเทอร์เฟซ ฉันเห็นด้วยและแสดงความคิดเห็นเกี่ยวกับความจริงที่ว่าเมื่อคุณเลือกที่จะเปิดเผยคลาสที่เป็นรูปธรรมในการโต้แย้งคุณจะสามารถสื่อสารเจตนาด้านประสิทธิภาพได้ การLinkedList<T>เทียบกับการList<T>เทียบIList<T>ทุกสื่อสารอะไรบางอย่างของการค้ำประกันผลการดำเนินงานที่จำเป็นโดยรหัสที่ถูกเรียกว่า
joshperry

28

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


1
วิธีคิดที่น่าสนใจมากเกี่ยวกับมัน และวิธีคิดที่ดีเมื่อเขียนโปรแกรม - ขอบคุณ
ถั่วลิสง

พูดได้ดี! เนื่องจากอินเทอร์เฟซเป็นวิธีการที่ยืดหยุ่นมากขึ้นคุณควรจะต้องพิสูจน์ให้เห็นว่า
Cory House

9
วิธีคิดที่ยอดเยี่ยม ยังฉันสามารถตอบได้: เหตุผลของฉันเรียกว่า AddRange ()
PPC

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

19

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

ตัวอย่างเช่นหากมีคลาส A และคลาส B ของคุณสืบทอดมาคุณจะไม่สามารถใช้รายการ <T>

class A : B, IList<T> { ... }

15

หลักการของ TDD และ OOP โดยทั่วไปคือการเขียนโปรแกรมไปยังส่วนต่อประสานที่ไม่ใช่การใช้งาน

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

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


3
หาก ExtendedList <T> ของคุณสืบทอดรายการ <T> จากนั้นคุณยังสามารถใส่มันไว้ใน List <T> contract ได้ อาร์กิวเมนต์นี้ใช้ได้เฉพาะเมื่อคุณเขียนการใช้งาน IList <T> ของคุณเองจาก sratch (หรืออย่างน้อยก็ไม่มีรายการสืบทอด <T>)
PPC

14
public void Foo(IList<Bar> list)
{
     // Do Something with the list here.
}

ในกรณีนี้คุณสามารถผ่านคลาสใดก็ได้ที่ใช้อินเทอร์เฟซ IList <Bar> หากคุณใช้ List <Bar> แทนจะสามารถส่งผ่านอินสแตนซ์ List <Bar> ได้เท่านั้น

วิธี IList <Bar> มีการรวมกันอย่างหลวม ๆ มากกว่าทาง List <Bar>


13

สมมติว่าไม่มีรายการคำถามใด ๆ เทียบกับ IList (หรือคำตอบ) กล่าวถึงความแตกต่างของลายเซ็น (ซึ่งเป็นสาเหตุที่ฉันค้นหาคำถามนี้ใน SO!)

ดังนั้นนี่คือวิธีการที่มีอยู่ในรายการที่ไม่พบใน IList อย่างน้อยที่สุดเท่ากับ. NET 4.5 (ประมาณปี 2015)

  • AddRange
  • AsReadOnly
  • binarySearch
  • ความจุ
  • ConvertAll
  • ที่มีอยู่
  • หา
  • findall
  • FindIndex
  • FindLast
  • FindLastIndex
  • แต่ละ
  • GetRange
  • InsertRange
  • LastIndexOf
  • ลบทั้งหมด
  • RemoveRange
  • ย้อนกลับ
  • ประเภท
  • toArray
  • TrimExcess
  • TrueForAll

1
ไม่จริงอย่างสมบูรณ์ ReverseและToArrayเป็นวิธีการขยายสำหรับ IEnumerable <T> interface ซึ่ง IList <T> มาจาก
Tarec

นั่นเป็นเพราะสิ่งเหล่านี้สมเหตุสมผลในListการใช้งานIListเท่านั้น
HankCa

12

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


7

เกิดอะไรขึ้นถ้า .NET 5.0 แทนที่ไปSystem.Collections.Generic.List<T> System.Collection.Generics.LinearList<T>.NET มักจะเป็นเจ้าของชื่อList<T>แต่พวกเขารับประกันว่าIList<T>เป็นสัญญา ดังนั้น IMHO เรา (atleast I) จึงไม่ควรใช้ชื่อของใครบางคน (แม้ว่าจะเป็น. NET ในกรณีนี้) และประสบปัญหาในภายหลัง

ในกรณีที่ใช้IList<T>งานผู้โทรจะต้องรับประกันสิ่งที่ต้องทำอยู่เสมอและผู้ดำเนินการมีอิสระที่จะเปลี่ยนการรวบรวมข้อมูลพื้นฐานให้เป็นการดำเนินการทางเลือกที่เป็นรูปธรรมของIList


7

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

IList<T> defines those methods (not including extension methods)

IList<T> ลิงก์ MSDN

  1. เพิ่ม
  2. ชัดเจน
  3. มี
  4. CopyTo
  5. GetEnumerator
  6. IndexOf
  7. แทรก
  8. ลบ
  9. RemoveAt

List<T> ใช้วิธีการทั้งเก้า (ไม่รวมถึงวิธีการขยาย) นอกเหนือจากนั้นมีวิธีการสาธารณะประมาณ 41 วิธีซึ่งมีน้ำหนักในการพิจารณาว่าจะใช้วิธีใดในใบสมัครของคุณ

List<T> ลิงก์ MSDN


4

คุณจะเนื่องจากการกำหนด IList หรือ ICollection จะเปิดขึ้นสำหรับการใช้งานอื่น ๆ ของอินเทอร์เฟซของคุณ

คุณอาจต้องการที่จะมี IOrderRepository ที่กำหนดชุดคำสั่งใน IList หรือ ICollection จากนั้นคุณสามารถมีการนำไปใช้งานหลายรูปแบบเพื่อจัดทำรายการคำสั่งซื้อตราบใดที่พวกเขาปฏิบัติตาม "กฎ" ที่กำหนดโดย IList หรือ ICollection ของคุณ


3

IList <> เป็นที่นิยมมากกว่าตามคำแนะนำของผู้โพสต์คนอื่น ๆ อย่างไรก็ตามทราบว่ามีข้อผิดพลาดใน. NET 3.5 sp 1เมื่อเรียกใช้ IList <> ผ่านมากกว่าหนึ่งรอบของอนุกรม / deserialization กับ WCF DataContractSerializer

ขณะนี้มี SP เพื่อแก้ไขข้อบกพร่องนี้: KB 971030


2

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

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


2

คุณสามารถดูอาร์กิวเมนต์นี้จากหลาย ๆ มุมรวมถึงหนึ่งในวิธีการ OO ล้วนๆซึ่งบอกว่าจะตั้งโปรแกรมกับส่วนต่อประสานไม่ใช่การใช้งาน ด้วยความคิดนี้การใช้ IList ทำตามหลักการเดียวกันกับการผ่านไปและการใช้อินเทอร์เฟซที่คุณกำหนดตั้งแต่เริ่มต้น ฉันเชื่อในความสามารถในการปรับขนาดและความยืดหยุ่นที่ได้รับจากอินเทอร์เฟซทั่วไป หากจำเป็นต้องขยายหรือเปลี่ยนแปลงคลาส ILM <T> ที่เกี่ยวข้อง implant รหัสการบริโภคไม่จำเป็นต้องเปลี่ยน รู้ว่าสัญญาส่วนต่อประสาน IList เป็นอย่างไร อย่างไรก็ตามการใช้การนำไปปฏิบัติที่เป็นรูปธรรมและ List <T> ในชั้นเรียนที่มีการเปลี่ยนแปลงอาจทำให้ต้องมีการเปลี่ยนรหัสการโทรเช่นกัน นี่เป็นเพราะคลาสที่ปฏิบัติตาม IList <T> รับประกันพฤติกรรมบางอย่างที่ไม่ได้รับการรับรองโดยประเภทที่เป็นรูปธรรมโดยใช้รายการ <T>

นอกจากนี้ยังมีอำนาจที่จะทำสิ่งที่ต้องการปรับเปลี่ยนการดำเนินงานที่เริ่มต้นของรายการ <T> ในระดับการใช้ <T> IList สำหรับพูด .Add, .Remove หรือวิธี IList อื่น ๆ จะช่วยให้นักพัฒนาจำนวนมากของความยืดหยุ่นและอำนาจที่กำหนดไว้ล่วงหน้าเป็นอย่างอื่น ตามรายการ <T>


0

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

รายการชื่อชั้นอาจมีการเปลี่ยนแปลงในกรอบสุทธิ. ถัดไป แต่อินเทอร์เฟซจะไม่เปลี่ยนแปลงเนื่องจากอินเทอร์เฟซเป็นสัญญา

โปรดทราบว่าหาก API ของคุณจะถูกใช้เฉพาะในลูป foreach เป็นต้นคุณอาจต้องการพิจารณาเพียงแค่เปิดเผย IEnumerable แทน

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