تحسين أداء تطبيقات C#: دليل للمبتدئين

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

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

ابدأ الدورة
```html

مقدمة: لماذا الأداء مهم منذ أول سطر من الكود

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

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

1. العمل مع السلاسل النصية: لماذا String سيء و StringBuilder جيد

واحدة من أكثر مشاكل الأداء شيوعًا في C# هي التعامل غير الصحيح مع السلاسل النصية. السلاسل النصية في .NET غير قابلة للتغيير (immutable). هذا يعني أن أي عملية دمج (concatenation) تنشئ كائن جديدًا من السلسلة النصية في الذاكرة، بينما يبقى الكائن القديم ليجمع القمامة المهملة.

المشكلة: الدمج في حلقة

لنلقِ نظرة على مثال سيء:

string result = "";
for (int i = 0; i < 1000; i++)
{
    result += i.ToString() + " ";
}
// هنا يتم إنشاء 1000 سلسلة نصية وسيطة!

كل تكرار للحلقة ينشئ سلسلة نصية جديدة. بالنسبة لـ 1000 تكرار، هذا يعني 1000 عملية تخصيص ذاكرة. إذا كان عدد التكرارات 100,000، فقد يتسبب التطبيق في "تجميد" أو يؤدي إلى استثناء OutOfMemoryException.

الحل: استخدام StringBuilder

تعمل الفئة StringBuilder مع مخزن مؤقت قابل للتغيير من الأحرف. إنها لا تنشئ كائنات جديدة في كل مرة يتم فيها إضافة نص.

using System.Text;

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append(i);
    sb.Append(" ");
}
string result = sb.ToString();

نصيحة: استخدم دائمًا StringBuilder إذا كنت تقوم بأكثر من 3-5 عمليات دمج، خاصة داخل الحلقات. بالنسبة لدمج بسيط لعدة سلاسل نصية (مثل string s = a + b + c;)، فإن مترجم C# يقوم بتحسين ذلك تلقائيًا إلى استدعاء واحد لـ string.Concat، لذلك لا حاجة لـ StringBuilder هنا.

2. تحسين LINQ: اختيار الطرق الصحيحة

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

مثال على استعلام LINQ "المكلف"

var numbers = Enumerable.Range(1, 1000000);
var countEven = numbers.Where(n => n % 2 == 0).Count();
// هنا Where() ينشئ مكررًا، و Count() يتكرر عليه.
// كل شيء طبيعي إذا تم تعداد المجموعة مرة واحدة فقط.

يحدث الخطأ عندما تستدعي عدة عمليات نهائية (Count, ToList, First, Any) على نفس تعبير LINQ:

var query = numbers.Where(n => n % 2 == 0);
int count1 = query.Count(); // المرور الأول
int count2 = query.Count(); // المرور الثاني!
// يتم تعداد المجموعة مرتين.

كل استدعاء لـ Count() أو ToList() يعيد تنفيذ عملية التصفية. إذا كان لديك تصفية معقدة أو مصدر بيانات هو قاعدة بيانات (Entity Framework)، فإن هذا يؤدي إلى استعلامات مزدوجة.

النهج الصحيح: التمثيل (Materialization)

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

var query = numbers.Where(n => n % 2 == 0).ToList(); // مرور واحد
int count1 = query.Count;
int count2 = query.Count;

نصيحة: استخدم .ToList() أو .ToArray() مباشرة بعد التصفية إذا كنت تنوي استدعاء عدة طرق (Count, First, Sum). لاستدعاء واحد، اتركه كما هو.

```

المدونات

توصيات الكتب