Select وSelectMany في C#: أيهما تختار للسرعة؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

Select وSelectMany في C#: ما الفرق وكيف يؤثر على السرعة؟



إذا سبق لك كتابة استعلامات LINQ بلغة C#، فبالتأكيد واجهت طريقتين متشابهتين ولكن مختلفتين جدًا: Select وSelectMany. للوهلة الأولى، كلاهما "يختار شيئًا" من المجموعة. لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل. الاختيار الخاطئ بينهما يمكن أن يحول كودك السريع إلى فوضى بطيئة، بينما الاختيار الصحيح يجعله أنيقًا وفائق السرعة.



في هذه المقالة، لن نكتفي بشرح النظرية. سنقوم باختبار أداء حقيقي، ونلقي نظرة على البنية الداخلية لكلا الطريقتين، ونقدم لك خوارزمية واضحة: متى تستخدم Select ومتى تستخدم SelectMany. هل أنت مستعد؟ لنبدأ!



نبذة سريعة: ما هما Select وSelectMany؟



في البداية - الأساسيات. تخيل أن لديك قائمة من الطلاب (List<Student>)، ولكل طالب قائمة من الدرجات (List<int>).



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



  • مثال: الحصول على أسماء جميع الطلاب.
  • التوقيع: IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)


SelectMany هو إسقاط "واحد إلى متعدد" مع "تسطيح" (flatten) لاحق. كل عنصر من المجموعة الأصلية يمكن أن ينتج عدة عناصر في المخرجات، ويتم دمجها جميعًا في قائمة مسطحة واحدة.



  • مثال: الحصول على جميع درجات جميع الطلاب (قائمة مسطحة واحدة من الأرقام).
  • التوقيع: IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector)


يبدو الفرق واضحًا. ولكن أين المكمن؟ المكمن هو في كيفية عمل هذه الطرق تحت الغطاء وكيف يخلط المبتدئون بينها غالبًا.



الفرق الرئيسي: شكل البيانات الناتجة



دعنا نثبت ذلك فورًا بمثال كود. إليك فئة النموذج الخاصة بنا:



public class Student{    public string Name { get; set; }    public List<int> Grades { get; set; } = new List<int>();}

// إنشاء البياناتvar students = new List<Student>{ new Student { Name = "آنا", Grades = new List<int>{ 5, 4, 5 } }, new Student { Name = "بوريس", Grades = new List<int>{ 3, 4 } }, new Student { Name = "فيكا", Grades = new List<int>{ 5, 5, 5, 4 } }};


مثال 1: استخدام Select (بشكل خاطئ)



// نريد الحصول على جميع الدرجات. لكن Select سيعيد مجموعة من المجموعات!var allGradesNested = students.Select(s => s.Grades);// النوع: IEnumerable<List<int>> — ثلاث قوائم بالداخل!

foreach (var gradeList in allGradesNested){ foreach (var grade in gradeList) { Console.Write(grade + " "); // يجب استخدام حلقة متداخلة }}// الإخراج: 5 4 5 3 4 5 5 5 4


هذا يعمل، لكنه غير مريح. تحصل على هيكل متداخل يجب تسطيحه بشكل إضافي.



مثال 2: استخدام SelectMany (بشكل صحيح)



// SelectMany يقوم بالعمل نيابة عنا: يأخذ المجموعات الداخلية و"يسطحها"var allGradesFlat = students.SelectMany(s => s.Grades);// النوع: IEnumerable<int> — قائمة مسطحة واحدة من الأرقام!

foreach (var grade in allGradesFlat){ Console.Write(grade + " "); // لا حاجة لحلقات متداخلة!}// الإخراج: 5 4 5 3 4 5 5 5 4


هل تشعر بالفرق

المدونات

توصيات الكتب