سؤال

يمكن لأي شخص أن يشرح لي لماذا كنت ترغب في استخدام IList أكثر من قائمة في C# ؟

ذات السؤال: لماذا تعتبر سيئة لفضح List<T>

هل كانت مفيدة؟

المحلول

إذا كنت تعريض الطبقة من خلال المكتبة أن الآخرين سوف تستخدم ، عموما تريد أن تعرض عليه عبر واجهات بدلا من الخرسانة تطبيقات.وهذا سوف يساعد إذا كنت ترغب في تغيير تنفيذ الفئة الخاصة بك في وقت لاحق إلى فئة محددة.في هذه الحالة مستخدمي المكتبة الخاصة بك لا تحتاج إلى تحديث التعليمات البرمجية الخاصة بهم منذ واجهة لا تغيير.

إذا كنت تستخدم فقط داخليا ، قد لا يهتمون كثيرا باستخدام List<T> قد تكون على ما يرام.

نصائح أخرى

أقل شعبية الجواب المبرمجين مثل التظاهر البرامج الخاصة بهم هو الذهاب إلى أن تكون إعادة استخدامها في جميع أنحاء العالم ، عندما في الواقع الغالبية العظمى من المشاريع سيتم الحفاظ على حسب كمية صغيرة من الناس و لكن واجهة جميلة ذات الصلة استطاعتهم أنك تخدع نفسك.

العمارة رواد الفضاء.احتمالات سوف من أي وقت مضى الكتابة الخاصة بك IList أن يضيف أي شيء منها بالفعل .NET framework حتى بعد أن النظرية جيلي توتس محفوظة ل "أفضل الممارسات".

Software astronauts

من الواضح إذا أن يطلب والتي يمكنك استخدامها في مقابلة تقول IList, ابتسامة, كل نظرة يسر في أنفسكم لكونه ذكي جدا.أو من أجل مواجهة API ، IList.نأمل لك وجهة نظري.

واجهة هو وعد (أو العقد).

كما هو الحال دائما مع الوعود - أصغر كلما كان ذلك أفضل.

بعض الناس يقولون: "دائما استخدام 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?
}

إذا كان شخص ما يمر عليك مجموعة أو قائمة التعليمات البرمجية الخاصة بك سوف تعمل بشكل جيد إذا تحقق العلم في كل مرة و قد تراجع...ولكن حقا ، من الذي يفعل ذلك ؟ لا تعرف مسبقا إذا كان الأسلوب يحتاج إلى القائمة التي يمكن أن تتخذ إضافية الأعضاء ؛ لا يمكنك تحديد ذلك في طريقة التوقيع ؟ ماذا كنت ستفعل لو كنت مرت قراءة قائمة فقط مثل int[]?

يمكنك استبدال List<T> في التعليمات البرمجية التي تستخدم IList<T>/ICollection<T> صحيح.لك لا ضمان أن يمكنك بديلا IList<T>/ICollection<T> في التعليمات البرمجية التي تستخدم List<T>.

هناك نداء إلى واحد المسؤولية مبدأ / واجهة الفصل المبدأ في الكثير من الحجج لاستخدام تجريدات بدلا من الخرسانة أنواع تعتمد على أضيق ممكن واجهة.في معظم الحالات, إذا كنت تستخدم List<T> وكنت تعتقد أنك يمكن أن تستخدم واجهة أضيق بدلا من ذلك - لماذا لا IEnumerable<T>?هذا هو في كثير من الأحيان أفضل مناسبا إذا كنت لا تحتاج إلى إضافة عناصر.إذا كنت بحاجة إلى إضافة إلى جمع واستخدام الخرسانة نوع ، List<T>.

بالنسبة لي IList<T>ICollection<T>) هو أسوأ جزء من .NET framework. IsReadOnly ينتهك مبدأ الأقل مفاجأة.فئة ، مثل Array, الذي لم يسمح مضيفا إدراج أو إزالة العناصر لا ينبغي أن تنفيذ واجهة مع إضافة إدراج وإزالة الأساليب.(انظر أيضا https://softwareengineering.stackexchange.com/questions/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 == true) يمكن أن يكون.

لمزيد من المعلومات حول IsReadOnly ، انظر msdn تعريف ICollection<T>.IsReadOnly

List<T> هو محدد تنفيذ IList<T>, وهو الحاويات التي يمكن معالجتها بنفس طريقة خطي صف T[] باستخدام فهرس عدد صحيح.عند تحديد IList<T> كنوع من الأسلوب حجة, يمكنك فقط تحديد تحتاج إلى قدرات معينة من الحاوية.

على سبيل المثال مواصفات واجهة لا فرض معين بنية البيانات المستخدمة.تنفيذ List<T> يحدث نفس الأداء للوصول إلى حذف وإضافة عناصر مجموعة خطية.ومع ذلك ، يمكن أن يتصور التنفيذ التي تدعمها قائمة مرتبطة بدلا من ذلك ، إضافة عناصر إلى النهاية هو أرخص (ثابت-الوقت) ولكن الوصول العشوائي أكثر تكلفة بكثير.(لاحظ أن .صافي LinkedList<T> لا لا تنفيذ IList<T>.)

هذا المثال يقول لك أيضا أنه قد تكون هناك حالات عندما تحتاج إلى تحديد التنفيذ ، وليس واجهة في القائمة الحجة:في هذا المثال, كلما كنت بحاجة إلى معين الوصول إلى الأداء المميز.هذا هو عادة مضمون محدد تنفيذ حاوية (List<T> الوثائق:"وتنفذ IList<T> عامة واجهة استخدام صفيف حجمها بشكل حيوي زيادة كما هو مطلوب.").

بالإضافة إلى ذلك, قد ترغب في النظر في تعريض أقل الوظائف التي تحتاج إليها.على سبيل المثال.إذا كنت لا تحتاج إلى تغيير محتوى القائمة ، ربما يجب عليك النظر في استخدام IEnumerable<T>, ، IList<T> يمتد.

أنا أحول السؤال حول قليلا ، بدلا من تبرير لماذا يجب عليك استخدام واجهة على تنفيذ ملموس ، في محاولة لتبرير لماذا استخدام الخرسانة التنفيذ بدلا من واجهة.إذا كنت لا يمكن أن تبرر استخدام واجهة.

IList<T> هو واجهة لذا أنت يمكن أن ترث فئة أخرى لا تزال تنفذ IList<T> بينما يرث قائمة<T> يمنعك أن تفعل ذلك.

على سبيل المثال إذا كان هناك فئة A و الفئة B يرث ومن ثم لا يمكنك استخدام قائمة<T>

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

مبدأ TDD و OOP عموما هو البرمجة إلى واجهة عدم التنفيذ.

في هذه الحالة المحددة منذ كنت أساسا نتحدث عن اللغة بناء وليس مخصص واحد عموما لا يهم, ولكن نقول على سبيل المثال أن وجدت قائمة لم تدعم شيء كنت في حاجة.إذا كنت قد استخدمت IList في بقية التطبيق يمكنك توسيع القائمة المخصصة الخاصة بك فئة لا يزال تكون قادرة على تمرير الذي حول دون إعادة بيع ديون.

تكلفة للقيام بذلك هو الحد الأدنى ، لماذا لا تنقذ نفسك من الصداع في وقت لاحق ؟ هذا ما واجهة المبدأ هو كل شيء.

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

في هذه الحالة يمكن أن تمر في أي الطبقة التي تنفذ IList<Bar> واجهة.إذا كنت تستخدم قائمة<Bar> بدلا من ذلك, فقط قائمة<Bar> على سبيل المثال يمكن أن تكون مرت في.

على IList<Bar> الطريقة هي أكثر المتباعدة من القائمة<Bar> الطريقة.

أهم قضية باستخدام واجهات أكثر تطبيقات في المعلمات إلى API الخاص بك.إذا API الخاص بك يأخذ قائمة المعلمة ، ثم أي شخص يستخدم ذلك أن استخدام القائمة.إذا كان نوع المعلمة IList ، ثم الطالب لديه حرية أكثر بكثير, و يمكن استخدام فئات لم تسمع عن الذي قد لا يكون موجودا عندما كان رمز مكتوب.

Supprising أن أيا من هذه القائمة مقابل IList الأسئلة (أو الإجابات) يذكر التوقيع الفرق.(والذي هو السبب في أنني بحثت عن هذا السؤال على ذلك!)

حتى هنا هو الطرق الوارد من القائمة التي لا توجد في IList على الأقل اعتبارا من .NET 4.5 (حوالي 2015)

  • AddRange
  • AsReadOnly
  • BinarySearch
  • القدرات
  • ConvertAll
  • موجود
  • البحث
  • FindAll
  • FindIndex
  • FindLast
  • FindLastIndex
  • ForEach
  • GetRange
  • InsertRange
  • LastIndexOf
  • RemoveAll
  • RemoveRange
  • عكس
  • نوع
  • ToArray
  • TrimExcess
  • TrueForAll

ماذا لو .صافي 5.0 محل System.Collections.Generic.List<T> إلى System.Collection.Generics.LinearList<T>..صافي دائما يملك اسم List<T> ولكن أنها تضمن أن IList<T> هو العقد.حتى IMHO نحن (على الاقل انا) ليس من المفترض أن استخدام اسم شخص ما (على الرغم من أنه هو .صافي في هذه الحالة) و ندخل في مشاكل في وقت لاحق.

في حالة استخدام IList<T>, المتصل دائما guareented الأشياء إلى العمل المنفذ هو الحرة للتغيير الأساسية جمع أي بديل ملموسة تنفيذ IList

كل المفاهيم هي في الأساس جاء في أكثر من الإجابات أعلاه بشأن لماذا استخدام واجهة على تطبيقات ملموسة.

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 الرابط

هل لأن تحديد IList أو ICollection وسوف تفتح تطبيقات أخرى من واجهات.

قد تريد أن يكون لها IOrderRepository أن يحدد مجموعة من الأوامر إما IList أو ICollection.ثم يمكن أن يكون لديك أنواع مختلفة من التطبيقات لتوفير قائمة من الأوامر طالما أنها تتفق مع "القواعد" التي يحددها الخاص بك IList أو ICollection.

واجهة يضمن لك الحصول على الأقل على أساليب كنت تتوقع;يجري على بينة من تعريف واجهة أي.جميع الأساليب التجريدية التي هي أن يكون هناك تنفيذها من قبل أي فئة وراثة واجهة.حتى لو كان بعض واحد يجعل فئة كبيرة من بلده مع عدة طرق إلى جانب تلك التي ورثها من واجهة إضافة بعض الوظائف ، وهؤلاء هم من لا استخدام لكم الأفضل استخدام إشارة إلى فئة فرعية (في هذه الحالة واجهة) و تعيين فئة محددة يعترض على ذلك.

ميزة إضافية هي أن التعليمات البرمجية الخاصة بك في مأمن من أي تغييرات على فئة محددة كما يمكنك الاشتراك في عدد قليل فقط من أساليب فئة محددة و تلك هي التي ستكون هناك طالما أن فئة محددة يرث من واجهة استخدام.لذلك السلامة لك الحرية المبرمج من كتابة التنفيذ الملموس إلى تغيير أو إضافة المزيد من الوظائف إلى فئة محددة.

IList<> هو دائما تقريبا الأفضل في آخر ملصق المشورة ، ومع ذلك ملاحظة هناك خلل في .NET framework 3.5 sp 1 عند تشغيل IList<> من خلال أكثر من دورة واحدة من التسلسل / إلغاء التسلسل مع WCF DataContractSerializer.

هناك الآن SP لإصلاح هذا الخلل : KB 971030

يمكنك أن تبحث في هذه الحجة من عدة زوايا بما في ذلك واحدة من بحتة OO النهج الذي يقول أن البرنامج ضد واجهة عدم التنفيذ.مع هذا الفكر ، باستخدام IList يتبع نفس المبدأ كما يمر حول استخدام الواجهات التي تحدد من الصفر.وأعتقد أيضا في التدرجية والمرونة العوامل التي تقدمها واجهة بشكل عام.إذا فئة implmenting IList<T> يحتاج إلى توسيع أو تغيير طويلا رمز لا يجب أن تتغير ؛ فهو يعرف ما IList واجهة عقد يلتزم.ومع ذلك باستخدام الخرسانة تنفيذ قائمة<T> على فئة أن التغييرات يمكن أن يسبب رمز الدعوة إلى ضرورة أن تتغير كذلك.وذلك لأن فئة التمسك IList<T> يضمن سلوك معين غير مضمون من قبل الخرسانة نوع باستخدام قائمة<T>.

أيضا وجود قوة أن تفعل شيئا مثل تعديل تطبيق الافتراضي من قائمة<T> على فئة تنفيذ IList<T> على قول .إضافة .إزالة أو أي IList الأسلوب يعطي المطور الكثير من المرونة والقوة ، وإلا محددة مسبقا من القائمة<T>

عادة, هو نهج جيد لاستخدام IList في العام تواجه API (عند الاقتضاء ، قائمة دلالات مطلوبة) ومن ثم قائمة داخليا لتنفيذ API.هذا يسمح لك بتغيير مختلفة تنفيذ IList دون كسر التعليمات البرمجية التي تستخدم الفئة الخاصة بك.

اسم الفئة القائمة قد يكون تغير في القادم .net framework ولكن واجهة لن تتغير واجهة العقد.

ملاحظة أنه إذا API الخاص بك فقط سوف تستخدم في foreach الحلقات, الخ, ثم قد ترغب في النظر في مجرد تعريض IEnumerable بدلا من ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top