التعامل مع المصفوفات والقوائم في C#: دليل شامل

التعامل مع المصفوفات والقوائم في C#: دليل شامل

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

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

ما هي المصفوفات في C#؟

المصفوفة في C# هي مجموعة من العناصر من نفس النوع، يتم تخزينها في مواقع ذاكرة متجاورة. حجم المصفوفة ثابت ويتم تحديده عند الإنشاء. يمكن أن تكون المصفوفات أحادية البعد، أو متعددة الأبعاد، أو متعرجة (jagged).

تعريف المصفوفة

المصفوفة هي كائن يرث من System.Array. توفر خصائص مثل Length وطرق مثل GetLength و Rank. جميع عناصر المصفوفة يجب أن تكون من نفس النوع، سواء كان نوعاً أولياً مثل int أو نوعاً مرجعياً مثل string.

أنواع المصفوفات

تدعم C# ثلاثة أنواع رئيسية من المصفوفات:

  • أحادية البعد: مصفوفة بسيطة ذات بعد واحد، مثل int[] numbers.
  • متعددة الأبعاد: مصفوفة ذات بعدين أو أكثر، مثل int[,] matrix.
  • متعرجة (Jagged): مصفوفة من المصفوفات، حيث يمكن أن يكون لكل عنصر حجم مختلف، مثل int[][] jagged.

هذا مثال يوضح إنشاء الأنواع الثلاثة:

// مصفوفة أحادية البعد
int[] numbers = new int[5];

// مصفوفة ثنائية الأبعاد
int[,] matrix = new int[3, 4];

// مصفوفة متعرجة
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5 };
jagged[2] = new int[] { 6 };

في المثال أعلاه، تم إنشاء مصفوفة أحادية البعد بحجم 5، ومصفوفة ثنائية الأبعاد بحجم 3×4، ومصفوفة متعرجة حيث كل صف يمكن أن يكون له طول مختلف.

إنشاء وتهيئة المصفوفات في C#

يمكن إنشاء المصفوفات بعدة طرق، اعتماداً على ما إذا كنت تعرف القيم مسبقاً أم لا. يمكن تهيئتها بقيم افتراضية أو بقيم محددة.

طرق الإنشاء

الطريقة الأساسية هي استخدام الكلمة المفتاحية new مع تحديد النوع والحجم:

int[] arr1 = new int[5]; // مصفوفة بحجم 5، جميع العناصر = 0
int[] arr2 = new int[] { 1, 2, 3, 4, 5 }; // تهيئة مباشرة
int[] arr3 = { 10, 20, 30 }; // صيغة مختصرة

يمكن أيضاً استخدام var للاستنتاج التلقائي للنوع:

var arr4 = new[] { 1, 2, 3 };

التهيئة بقيم افتراضية

عند إنشاء مصفوفة باستخدام new int[5]، يتم تهيئة جميع العناصر بالقيمة الافتراضية للنوع. للأنواع الرقمية تكون 0، وللأنواع المنطقية false، وللأنواع المرجعية null.

bool[] flags = new bool[3]; // [false, false, false]
string[] names = new string[2]; // [null, null]

يجب الحذر عند التعامل مع المصفوفات المرجعية، لأن العناصر تكون null حتى يتم تعيينها.

الوصول إلى عناصر المصفوفة وتعديلها

يتم الوصول إلى عناصر المصفوفة باستخدام الفهارس (indices)، بدءاً من الصفر. يمكن قراءة القيم أو تعديلها.

الوصول بالفهرس

لقراءة عنصر، استخدم اسم المصفوفة متبوعاً بالفهرس بين قوسين مربعين:

int[] numbers = { 10, 20, 30, 40, 50 };
int first = numbers[0]; // 10
int third = numbers[2]; // 30

تعديل العناصر

يمكن تعديل قيمة عنصر عن طريق إسناد قيمة جديدة له:

numbers[1] = 25; // تصبح المصفوفة { 10, 25, 30, 40, 50 }

تأكد من أن الفهرس ضمن الحدود المسموحة (من 0 إلى Length - 1)، وإلا ستحدث استثناء IndexOutOfRangeException.

الخصائص والطرق الهامة للمصفوفات

توفر المصفوفات في C# مجموعة من الخصائص والطرق الثابتة التي تسهل التعامل معها.

خاصية Length

تُرجع Length عدد العناصر الكلي في المصفوفة. للمصفوفات متعددة الأبعاد، استخدم GetLength(dimension).

int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine(numbers.Length); // 5

int[,] matrix = new int[3, 4];
Console.WriteLine(matrix.GetLength(0)); // 3
Console.WriteLine(matrix.GetLength(1)); // 4

طرق Array الثابتة

توفر فئة System.Array طرقاً ثابتة للفرز والبحث والنسخ:

  • Array.Sort(array): لفرز المصفوفة تصاعدياً.
  • Array.Reverse(array): لعكس ترتيب العناصر.
  • Array.IndexOf(array, value): لإرجاع فهرس أول تواجد للقيمة.
  • Array.Copy(source, destination, length): لنسخ العناصر.
  • Array.Clear(array, index, length): لتعيين عناصر إلى القيمة الافتراضية.
int[] numbers = { 5, 2, 8, 1, 9 };
Array.Sort(numbers); // { 1, 2, 5, 8, 9 }
Array.Reverse(numbers); // { 9, 8, 5, 2, 1 }
int index = Array.IndexOf(numbers, 5); // 2

مقدمة إلى القوائم (Lists) في C#

القوائم هي جزء من مساحة الأسماء System.Collections.Generic، وتوفر مرونة أكبر من المصفوفات لأن حجمها ديناميكي. يمكن إضافة عناصر وحذفها دون الحاجة إلى إعادة إنشاء القائمة.

تعريف List<T>

List<T> هي فئة عامة (generic) تمثل قائمة قوية النوع. حيث T هو نوع العناصر. تدعم جميع عمليات المصفوفات بالإضافة إلى الإضافة والحذف والإدراج.

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

  • الحجم: المصفوفة ثابتة، القائمة ديناميكية.
  • الأداء: المصفوفات أسرع قليلاً في الوصول المباشر، لكن القوائم توفر طرقاً أكثر.
  • المرونة: القوائم تسمح بالإضافة والحذف بسهولة.
  • التحويل: يمكن التحويل بينهما بسهولة.
using System.Collections.Generic;

List<int> numbers = new List<int>();
numbers.Add(10);
numbers.Add(20);
numbers.Add(30);

Console.WriteLine(numbers[1]); // 20
Console.WriteLine(numbers.Count); // 3

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

لإنشاء قائمة، يمكنك استخدام المُنشئ الافتراضي أو تمرير مجموعة أولية.

إنشاء قائمة فارغة

List<string> names = new List<string>();

إضافة عناصر

يمكن إضافة عنصر واحد باستخدام Add، أو مجموعة باستخدام AddRange، أو إدراج في موقع محدد باستخدام Insert.

List<int> numbers = new List<int>();
numbers.Add(5); // [5]
numbers.AddRange(new int[] { 10, 15 }); // [5, 10, 15]
numbers.Insert(1, 7); // [5, 7, 10, 15]

لاحظ أن Insert تزيد Count وتزحزح العناصر التالية.

التعامل مع عناصر القوائم (الوصول، التعديل، الحذف)

تشبه القوائم المصفوفات في الوصول والتعديل، لكنها توفر طرقاً أكثر للحذف.

الوصول والتعديل

List<string> fruits = new List<string> { \"Apple\", \"Banana\", \"Cherry\" };
string first = fruits[0]; // \"Apple\"
fruits[1] = \"Blueberry\"; // [\"Apple\", \"Blueberry\", \"Cherry\"]

الحذف

يمكن الحذف بعدة طرق:

  • Remove(value): يحذف أول تواجد للقيمة.
  • RemoveAt(index): يحذف العنصر عند الفهرس المحدد.
  • RemoveAll(predicate): يحذف جميع العناصر التي تحقق شرطاً.
  • Clear(): يحذف جميع العناصر.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 2 };
numbers.Remove(2); // يحذف أول 2 -> [1, 3, 4, 5, 2]
numbers.RemoveAt(0); // يحذف 1 -> [3, 4, 5, 2]
numbers.RemoveAll(x => x % 2 == 0); // يحذف 4 و 2 -> [3, 5]
numbers.Clear(); // []

البحث والفرز في القوائم

توفر القوائم طرقاً متعددة للبحث والفرز، بالإضافة إلى دعم LINQ.

البحث

  • Contains(value): يتحقق من وجود القيمة.
  • IndexOf(value): يرجع الفهرس أو -1.
  • Find(predicate): يرجع أول عنصر يحقق الشرط.
  • FindAll(predicate): يرجع قائمة بجميع العناصر التي تحقق الشرط.
  • Exists(predicate): يتحقق من وجود عنصر يحقق الشرط.
List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };
bool has30 = numbers.Contains(30); // true
int index = numbers.IndexOf(40); // 3
int firstEven = numbers.Find(x => x % 2 == 0); // 10
List<int> evenNumbers = numbers.FindAll(x => x % 2 == 0); // [10, 20, 30, 40, 50]
bool anyOdd = numbers.Exists(x => x % 2 != 0); // false

الفرز

يمكن فرز القائمة باستخدام Sort()، ويمكن تمرير دالة مقارنة مخصصة. أيضاً Reverse() لعكس الترتيب.

List<int> numbers = new List<int> { 5, 2, 8, 1, 9 };
numbers.Sort(); // [1, 2, 5, 8, 9]
numbers.Reverse(); // [9, 8, 5, 2, 1]

// فرز مخصص: تنازلي
numbers.Sort((a, b) => b.CompareTo(a)); // [9, 8, 5, 2, 1]

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

في كثير من الأحيان، تحتاج إلى التحويل بين المصفوفات والقوائم للاستفادة من ميزات كل منهما.

من مصفوفة إلى قائمة

int[] array = { 1, 2, 3 };
List<int> list = new List<int>(array);
// أو باستخدام ToList من LINQ
using System.Linq;
List<int> list2 = array.ToList();

من قائمة إلى مصفوفة

List<int> list = new List<int> { 1, 2, 3 };
int[] array = list.ToArray();

هذه التحويلات تنسخ العناصر، لذا أي تعديل لاحق على أحدهما لا يؤثر على الآخر.

مقارنة الأداء بين المصفوفات والقوائم

اختيار الهيكل المناسب يعتمد على متطلبات الأداء والمرونة.

الذاكرة والسرعة

  • المصفوفات: تخصيص ذاكرة مستمرة، وصول مباشر أسرع، لا يوجد عبء إضافي لإدارة الحجم.
  • القوائم: تستخدم مصفوفة داخلية تنمو ديناميكياً. عند إضافة عنصر يتجاوز السعة، يتم إنشاء مصفوفة جديدة ونسخ العناصر، مما يسبب عبئاً إضافياً.

متى تستخدم كل منهما

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

يمكن تحسين أداء القوائم عن طريق تحديد سعة ابتدائية (Capacity) لتقليل عمليات إعادة التخصيص.

List<int> numbers = new List<int>(1000); // سعة ابتدائية 1000

أفضل الممارسات عند استخدام المصفوفات والقوائم في C#

  • استخدم List<T> بدلاً من ArrayList غير العامة لتحقيق أمان النوع والأداء الأفضل.
  • عند استخدام المصفوفات، تحقق دائماً من الحدود قبل الوصول إلى العناصر.
  • استخدم foreach للقراءة فقط، و for عندما تحتاج إلى تعديل العناصر أو الفهرس.
  • حدد سعة ابتدائية للقوائم إذا كنت تعرف العدد التقريبي للعناصر.
  • استخدم Array.Empty<T>() بدلاً من إنشاء مصفوفة فارغة جديدة.
  • تجنب تحويل القوائم إلى مصفوفات بشكل متكرر في الحلقات.
  • استخدم Span<T> و Memory<T> لتحسين الأداء عند التعامل مع أجزاء من المصفوفات.

أخطاء شائعة وكيفية تجنبها

  • IndexOutOfRangeException: يحدث عند الوصول إلى فهرس غير موجود. تجنب بفحص index >= 0 && index < array.Length.
  • NullReferenceException: عند التعامل مع مصفوفة من الأنواع المرجعية لم يتم تهيئة عناصرها. تأكد من تعيين قيم لكل عنصر.
  • تعديل القائمة أثناء التكرار: باستخدام foreach مع Remove يسبب InvalidOperationException. استخدم حلقة for عكسية أو RemoveAll أو أنشئ نسخة.
  • الخلط بين Length و Count: Length للمصفوفات، Count للقوائم. استخدم الاسم الصحيح.
  • نسيان أن المصفوفات ثابتة الحجم: لا يمكن إضافة عناصر مباشرة. استخدم Array.Resize (الذي ينشئ مصفوفة جديدة) أو استخدم قائمة.

اعتبارات الأداء والأمان

عند التعامل مع المصفوفات والقوائم، يجب مراعاة الأداء والأمان لتجنب المشاكل.

الأداء

  • استخدم Array.Copy لنسخ المصفوفات بسرعة بدلاً من الحلقات.
  • تجنب استخدام LINQ في الحلقات الضيقة إذا كان الأداء حرجاً.
  • استخدم List<T>.Capacity لتقليل عمليات إعادة التخصيص.
  • استخدم Span<T> لتقسيم المصفوفات دون نسخ.

الأمان

  • تحقق دائماً من الحدود لمنع الثغرات الأمنية مثل تجاوز سعة المخزن المؤقت (buffer overflow).
  • استخدم Array.Clear لمسح البيانات الحساسة من المصفوفات بعد استخدامها.
  • عند استخدام القوائم، تأكد من أن العناصر لا تحتوي على بيانات حساسة قبل مشاركتها.

هذا مثال على التحقق الآمن من الحدود:

int[] numbers = { 1, 2, 3 };
int index = 5;
if (index >= 0 && index < numbers.Length)
{
    Console.WriteLine(numbers[index]);
}
else
{
    Console.WriteLine(\"الفهرس خارج الحدود\");
}

استخدامات واقعية للمصفوفات والقوائم

تظهر المصفوفات والقوائم في العديد من السيناريوهات العملية:

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

اختر الهيكل المناسب بناءً على طبيعة البيانات والعمليات المطلوبة.

التعامل مع المصفوفات متعددة الأبعاد بالتفصيل

المصفوفات متعددة الأبعاد تسمح بتخزين البيانات في شكل شبكة. أكثر الأنواع شيوعاً هي المصفوفات ثنائية الأبعاد، والتي تشبه الجداول.

الوصول إلى العناصر في مصفوفة ثنائية الأبعاد

يتم الوصول إلى عنصر في مصفوفة ثنائية الأبعاد باستخدام فهرسين: فهرس الصف وفهرس العمود.

int[,] matrix = new int[3, 4];
matrix[0, 0] = 1;
matrix[1, 2] = 5;
int value = matrix[1, 2]; // 5

التكرار عبر المصفوفات متعددة الأبعاد

يمكن استخدام حلقتين متداخلتين للتكرار عبر المصفوفة ثنائية الأبعاد. استخدم GetLength(0) للصفوف و GetLength(1) للأعمدة.

for (int i = 0; i < matrix.GetLength(0); i++)
{
    for (int j = 0; j < matrix.GetLength(1); j++)
    {
        Console.Write(matrix[i, j] + \" \");
    }
    Console.WriteLine();
}

هذا سيطبع جميع عناصر المصفوفة صفاً بصف.

استخدام LINQ مع المصفوفات والقوائم

يوفر LINQ (Language Integrated Query) طريقة موحدة للاستعلام عن البيانات من مصادر مختلفة، بما في ذلك المصفوفات والقوائم. يمكن استخدام LINQ لتصفية وترتيب وتجميع العناصر.

استعلامات LINQ الأساسية

للاستفادة من LINQ، أضف using System.Linq; في بداية الملف.

int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// تصفية الأرقام الزوجية
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();

// ترتيب تنازلي
var sortedDesc = numbers.OrderByDescending(n => n).ToList();

// تجميع حسب الباقي
var grouped = numbers.GroupBy(n => n % 2)
                     .Select(g => new { Remainder = g.Key, Numbers = g.ToList() });

foreach (var group in grouped)
{
    Console.WriteLine($\"الباقي {group.Remainder}: {string.Join(\", \", group.Numbers)}\");
}

هذه الاستعلامات تعمل على كل من المصفوفات والقوائم، وتوفر طريقة تعبيرية لمعالجة البيانات.

الفروق بين List<T> و ArrayList و Array

في C#، هناك عدة أنواع من المجموعات، ولكل منها استخداماته.

  • Array: ثابت الحجم، قوي النوع، أسرع وصول، استهلاك أقل للذاكرة.
  • ArrayList: ديناميكي، غير عام (يخزن object)، يتطلب تحويلاً للنوع، أبطأ من List<T>.
  • List<T>: ديناميكي، عام، آمن النوع، يوفر طرقاً غنية، أداء جيد.

بشكل عام، يفضل استخدام List<T> على ArrayList في الكود الجديد.

نصائح لتحسين الأداء عند التعامل مع مجموعات كبيرة

عند التعامل مع ملايين العناصر، يصبح الأداء عاملاً حاسماً.

  • استخدم المصفوفات عندما يكون الحجم ثابتاً، لأنها أسرع.
  • حدد سعة ابتدائية للقائمة لتجنب إعادة التخصيص المتكرر.
  • استخدم for بدلاً من foreach إذا كنت تحتاج إلى الفهرس، لتجنب إنشاء المُعدِّد (enumerator).
  • استخدم ArrayPool<T> لإعادة استخدام المصفوفات الكبيرة.
  • تجنب العمليات التي تسبب نسخاً غير ضروري، مثل ToList() داخل الحلقات.
  • استخدم Span<T> و Memory<T> للتعامل مع الشرائح دون تخصيص ذاكرة إضافية.

التعامل مع المصفوفات والقوائم في سيناريوهات متقدمة

هناك تقنيات متقدمة يمكن أن تحسن الأداء وتجعل الكود أكثر كفاءة.

استخدام Span<T> و Memory<T>

Span<T> هو نوع مرجعي خفيف الوزن يمثل جزءاً مستمراً من الذاكرة. يمكن استخدامه للوصول إلى أجزاء من مصفوفة دون نسخ.

int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> slice = numbers.AsSpan(1, 3); // العناصر 2, 3, 4
slice[0] = 20; // يعدل المصفوفة الأصلية: { 1, 20, 3, 4, 5 }

هذا مفيد جداً في معالجة النصوص والبيانات الثنائية.

المصفوفات المتوازية (Parallel Arrays)

هي استخدام عدة مصفوفات لتخزين بيانات مرتبطة، حيث يكون العنصر في نفس الفهرس مرتبطاً بالعناصر في المصفوفات الأخرى.

string[] names = { \"Ali\", \"Sara\", \"Omar\" };
int[] ages = { 25, 30, 28 };
for (int i = 0; i < names.Length; i++)
{
    Console.WriteLine($\"{names[i]} is {ages[i]} years old.\");
}

لكن يفضل استخدام قائمة من الكائنات (List<Person>) لتحسين التنظيم.

الأسئلة الشائعة حول التعامل مع المصفوفات والقوائم في C#

ما الفرق بين المصفوفة والقائمة في C#؟

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

متى يجب استخدام المصفوفة بدلاً من القائمة؟

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

كيف يمكنني إضافة عنصر إلى مصفوفة في C#؟

لا يمكن إضافة عنصر إلى مصفوفة مباشرة لأن حجمها ثابت. يمكنك استخدام Array.Resize لإنشاء مصفوفة جديدة بحجم أكبر ونسخ العناصر، أو ببساطة استخدام List<T> إذا كنت بحاجة إلى إضافة عناصر بشكل متكرر.

كيف يمكنني فرز قائمة في C#؟

يمكنك استخدام طريقة Sort() لفرز القائمة تصاعدياً. لفرز مخصص، مرر دالة مقارنة Comparison<T>. كما يمكن استخدام OrderBy من LINQ لإنشاء تسلسل جديد مرتب.

ما هي أفضل طريقة للبحث في قائمة؟

يعتمد ذلك على احتياجك. استخدم Contains للتحقق من الوجود، IndexOf للحصول على الفهرس، Find لإرجاع أول عنصر يحقق شرطاً، و FindAll لإرجاع جميع العناصر التي تحقق الشرط. للبحث المعقد، استخدم LINQ مع Where.

الخلاصة والخطوات التالية

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

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

ابدأ اليوم بتحسين كودك باستخدام المصفوفات والقوائم بفعالية، وستلاحظ فرقاً كبيراً في جودة وأداء تطبيقاتك.

#c# #المصفوفات في c# #القوائم في c# #list<t> #array #برمجة #دوت نت #تعلم البرمجة #هياكل البيانات #أداء #أفضل الممارسات

اشترك في نشرتنا البريدية

12k+

المشتركون

أسبوعيًا

التكرار

مجاني

دائمًا