Menu
flag Ar iconالعربيةdown icon

القائمة List في C#: Add وRemove وContains وFind وSort

List<T> هي المصفوفة القابلة للنمو في C#. تعلّم كيف تنشئ قائمة، وتضيف العناصر وتدرجها، وتحذف بالقيمة أو الفهرس أو الشرط، وتبحث بـ Contains وFind، وتفرز بحسب خاصية، وتتجنّب الخطأ الناتج عن تغيير قائمة داخل foreach.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

List<T> مجموعة مرتّبة تكبر وتصغر كلما أضفت عناصر وحذفتها. تعيش في System.Collections.Generic، وتُفهرس مثل المصفوفة (list[0])، وهي الخيار الافتراضي لـ "مجموعة من الأشياء" في C#.

إنشاء قائمة وإضافة العناصر

يحدد معامل النوع بين علامتي الزاوية ما تحمله القائمة: List<string> وList<int> وList<Order>.

المخرجات:

2
Zoe, Ana, Ben, Chloe, Dev
Ana
Ana B.
3

Count خاصية لا دالة (تستخدم المصفوفات Length، وفي LINQ دالة Count()؛ أما مع القائمة فاستخدم الخاصية). تزيح Insert كل عنصر لاحق موضعًا واحدًا، فيكلّف الإدراج في مقدمة قائمة كبيرة وقتًا يتناسب مع حجمها. أما Add في النهاية فهي العملية الرخيصة.

حذف العناصر

هناك أربع طرق للحذف، بحسب ما تعرفه عن العنصر:

المخرجات:

True: 85, 62, 40, 91, 55
False
62, 40, 91, 55
2 removed: 62, 91
Caught ArgumentOutOfRangeException
0

تأخذ Remove قيمة ولا تحذف إلا أول تطابق. لحذف كل نسخ قيمة استخدم RemoveAll(s => s == 40). الفهرس الخاطئ في القائمة يرمي ArgumentOutOfRangeException (أما المصفوفة فترمي IndexOutOfRangeException)، في list[i] وفي RemoveAt(i) كليهما. وتحذف RemoveRange(index, count) كتلة.

Count مقابل Capacity

تحفظ القائمة عناصرها في مصفوفة داخلية. Capacity حجم تلك المصفوفة؛ وCount عدد الخانات المستخدمة. حين يبلغ Count قيمة Capacity تحجز Add التالية مصفوفة بضعف الحجم وتنسخ كل شيء إليها.

المخرجات:

Count 0, Capacity 0
Count 1, Capacity 4
Count 2, Capacity 4
Count 3, Capacity 4
Count 4, Capacity 4
Count 5, Capacity 8
Count 6, Capacity 8
Count 7, Capacity 8
Count 8, Capacity 8
Count 9, Capacity 16
Count 0, Capacity 1000

المضاعفة تعني أن معظم استدعاءات Add مجرد كتابة في خانة فارغة. إن كنت تعرف تقريبًا عدد العناصر القادمة فمرّر العدد إلى المُنشئ لتتخطى النسخ الوسيطة. لاحظ أن new List<int>(1000) ما زال Count فيها 0: list[0] عليها ترمي استثناءً. السعة مساحة لا عناصر.

البحث: Contains وIndexOf وFind وExists

المخرجات:

True
2
-1
32.00
2 cheap items
False
2
True
False

تعيد Find أول تطابق أو القيمة الافتراضية للنوع (null للصنف) حين لا يطابق شيء، فافحص قبل استخدام النتيجة. يطبع السطر الأخير False لأن Contains تستخدم Equals، والصنف الذي لا يتجاوز Equals يقارن المراجع: Product جديد بالحقول نفسها كائن مختلف. ابحث بحسب خاصية بـ Exists أو Find بدلًا من ذلك، أو تجاوز Equals وGetHashCode في الصنف.

كل واحدة من هذه الدوال تمشي على القائمة من البداية، فكل استدعاء O(n). وللبحث المتكرر بمفتاح يجيب Dictionary في زمن ثابت.

فرز قائمة

تفرز Sort() في المكان بالترتيب الطبيعي للعناصر. مرّر تعبير lambda للمقارنة لتفرز بأي شيء آخر.

المخرجات:

1, 2, 5, 9
9, 5, 2, 1
Ben 95, Ana 120, Chloe 150
Chloe 150, Ben 95, Ana 120
Ben 95, Ana 120, Chloe 150

تعيد المقارنة رقمًا سالبًا حين يجب أن يأتي a أولًا، وموجبًا حين يجب أن يأتي b، وصفرًا عند التعادل؛ وCompareTo تنتج ذلك بالضبط. فرقان بين Sort وOrderBy في LINQ يهمّان عمليًا:

  • تغيّر Sort القائمة؛ وتعيد OrderBy تسلسلًا جديدًا وتترك القائمة كما هي.
  • Sort غير مستقرة: قد تتبادل العناصر المتساوية أماكنها. أما OrderBy فمستقرة، وتضيف ThenBy مفتاحًا ثانيًا: players.OrderBy(p => p.Score).ThenBy(p => p.Name).

استدعاء Sort() على قائمة من صنفك دون مقارنة يرمي InvalidOperationException، لأن القائمة لا تعرف كيف ترتّب العناصر. إما أن تمرّر مقارنة أو تنفّذ IComparable<T> في الصنف.

تعكس Reverse() في المكان. ولأن System.Linq تعرّف أيضًا توسيع Reverse، فإن list.Reverse() على List<T> تختار النسخة التي تعمل في المكان، والتي لا تعيد شيئًا.

المرور، والحذف أثناء المرور

تقرأ foreach كل عنصر بالترتيب. وتمنحك حلقة for الفهرس أيضًا. ما لا تستطيعه هو إضافة عناصر أو حذفها في منتصف foreach على القائمة نفسها:

المخرجات:

Caught InvalidOperationException
120, 80, 60
120, 80, 60

الاستثناء InvalidOperationException برسالة "Collection was modified; enumeration operation may not execute." حلقة for الأمامية لا ترمي، لكنها تتخطى بصمت العنصر الذي يلي كل عنصر تحذفه، لأن كل شيء يزاح فهرسًا واحدًا للخلف. المرور المعكوس يتجنّب ذلك. والحل الثالث المرور على نسخة: foreach (var t in orders.ToList()).

تغيير خاصية في عنصر داخل foreach (مثل order.Status = "sent") مقبول. أما إضافة العناصر أو حذفها فتكسر المعدِّد، وكذلك استبدال عنصر عبر المفهرس (orders[i] = 0).

التحويل بين القوائم والمصفوفات

المخرجات:

4
3
4, 4, 4, 4
Oslo / Lima / Pune / Kyiv

الاتجاهان كلاهما ينسخ العناصر إلى تخزين جديد، فتغيير القائمة لاحقًا لا يؤثر في المصفوفة. ConvertAll هي نسخة القائمة الخاصة من Select(...).ToList() في LINQ. وطباعة قائمة بـ Console.WriteLine(list) تُظهر اسم النوع (System.Collections.Generic.List`1[System.String])؛ استخدم string.Join.

مرجع سريع

المهمةالشيفرة
الإنشاءvar l = new List<int>(); أو new List<int> { 1, 2 }
الإضافة في النهايةl.Add(x)، l.AddRange(items)
الإدراج في موضعl.Insert(i, x)
حذف أول تطابقl.Remove(x) (تعيد bool)
الحذف بالفهرسl.RemoveAt(i)
الحذف بشرطl.RemoveAll(x => ...) (تعيد العدد)
عدد العناصرl.Count
الاحتواءl.Contains(x)، l.Exists(x => ...)
البحثl.Find(...)، l.FindAll(...)، l.FindIndex(...)
الفرز في المكانl.Sort()، l.Sort((a, b) => ...)
نسخة مفروزةl.OrderBy(x => ...).ToList()
إلى مصفوفةl.ToArray()

أخطاء شائعة

  • الحذف داخل foreach. يرمي InvalidOperationException؛ استخدم RemoveAll أو حلقة for معكوسة.
  • توقّع أن تحذف Remove(x) كل النسخ. تحذف الأولى؛ استخدم RemoveAll.
  • استخدام Contains على كائنات بلا Equals. تقارن المراجع؛ ابحث بحسب خاصية بـ Exists.
  • الفهرسة في قائمة أُنشئت بسعة. new List<int>(10) فارغة؛ أضف العناصر أولًا.
  • افتراض أن Sort تبقي المتعادلات بترتيبها. لا تفعل؛ استخدم OrderBy حين يهمّ الترتيب بين المتساويات.
  • إسناد قائمة لمشاركتها ثم التفاجؤ بالتغييرات. var copy = list; هي القائمة نفسها؛ وnew List<T>(list) نسخة.

الأسئلة الشائعة

كيف أضيف عناصر إلى List في C#؟

تُلحق list.Add(item) عنصرًا واحدًا في النهاية، وتُلحق list.AddRange(otherCollection) عناصر كثيرة، وتضع list.Insert(index, item) عنصرًا في موضع وتزيح البقية. ويمكنك أيضًا ملء القائمة عند إنشائها: var names = new List<string> { "Ana", "Ben" };.

كيف أحذف عنصرًا من List في C#؟

تحذف Remove(value) أول عنصر مطابق وتعيد true إن وجدت واحدًا. وتحذف RemoveAt(index) بالموضع. وتحذف RemoveAll(x => condition) كل عنصر مطابق وتعيد عدد ما حذفته. وتفرغ Clear() القائمة.

كيف أتحقق أن List تحتوي على قيمة في C#؟

تعيد list.Contains(value) القيمة true أو false. تقارن بـ Equals، ففي أصنافك تقارن المراجع ما لم يتجاوز الصنف Equals. للفحص بحسب خاصية استخدم list.Exists(p => p.Name == "Ana") أو list.Any(...) في LINQ.

كيف أفرز List بحسب خاصية في C#؟

مرّر مقارنة إلى Sort: تفرز people.Sort((a, b) => a.Age.CompareTo(b.Age)) في المكان. بدّل a وb للترتيب التنازلي. أو استخدم LINQ، الذي يعيد تسلسلًا مفروزًا جديدًا ويبقي العناصر المتساوية بترتيبها الأصلي: people.OrderBy(p => p.Age).ToList().

لماذا أحصل على "Collection was modified; enumeration operation may not execute"؟

أضفت عناصر أو حذفتها أو استبدلتها (list[i] = x) بينما كانت foreach تمرّ على القائمة نفسها، فيرمي المعدِّد InvalidOperationException في خطوته التالية. استخدم list.RemoveAll(condition)، أو مرّ بشكل معكوس بحلقة for، أو مرّ على نسخة مثل list.ToList().

ما الفرق بين Count وCapacity؟

Count عدد العناصر في القائمة. وCapacity حجم المصفوفة الداخلية، وهو على الأقل Count. حين تمتلئ المصفوفة تحجز القائمة مصفوفة جديدة بضعف الحجم وتنسخ العناصر، فتكون Add سريعة في المتوسط.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن