مقدمة: لماذا الأداء مهم من البداية؟
\nكل مطور C# يواجه عاجلاً أم آجلاً موقفاً يبدأ فيه التطبيق بالتباطؤ. غالباً ما تُزرع مشاكل الأداء في المراحل المبكرة - بسبب الخوارزميات غير المثلى، أو التعامل غير الصحيح مع الذاكرة، أو الإفراط في استخدام LINQ دون فهم آلياته الداخلية. بالنسبة للمبتدئين، قد يبدو هذا الموضوع معقداً، لكن في الواقع المبادئ الأساسية للتحسين بسيطة بما يكفي ويمكن إتقانها في بضع ساعات من الممارسة.
\nفي هذه المقالة، سنناقش أربعة جوانب رئيسية ستساعدك على كتابة تطبيقات C# سريعة وفعالة على منصة .NET. ستتعلم كيفية تنميط الكود، تحسين استعلامات LINQ، إدارة الذاكرة بشكل صحيح، واستخدام البرمجة غير المتزامنة. ستكون أمثلة الكود قريبة قدر الإمكان من المهام الحقيقية التي يواجهها المطورون.
\n\n1. التنميط: الخطوة الأولى نحو التحسين
\nقبل تحسين أي شيء، تحتاج إلى فهم ما الذي يسبب التباطؤ بالضبط. التخمينات البديهية غالباً ما تكون خاطئة. التنميط هو عملية قياس أداء الكود الخاص بك باستخدام أدوات خاصة.
\n\nأدوات التنميط
\n- \n
- Visual Studio Diagnostic Tools - أداة مدمجة متوفرة في إصدارات Community والإصدارات الأعلى. تتيح تتبع استخدام وحدة المعالجة المركزية والذاكرة ووقت التنفيذ. \n
- JetBrains dotMemory / dotTrace - أدوات تجارية قوية لتحليل الذاكرة ووقت التنفيذ. \n
- BenchmarkDotNet - مكتبة خفيفة الوزن للاختبارات المعيارية الدقيقة. مثالية لمقارنة تطبيقين لطريقة واحدة. \n
مثال: قياس وقت التنفيذ
\nنستخدم Stopwatch لقياس بسيط:
using System.Diagnostics;\n\nvar stopwatch = Stopwatch.StartNew();\n// الكود الخاص بك\nfor (int i = 0; i < 1000000; i++)\n{\n // عملية ما\n}\nstopwatch.Stop();\nConsole.WriteLine($"وقت التنفيذ: {stopwatch.ElapsedMilliseconds} مللي ثانية");\nنصيحة: للقياسات الدقيقة، استخدم BenchmarkDotNet، لأن Stopwatch لا يأخذ في الاعتبار النفقات العامة لترجمة JIT.
2. التعامل مع الذاكرة والسلاسل النصية
\nإدارة الذاكرة في .NET آلية (جامع القمامة)، لكن هذا لا يعني أنه يمكنك تجاهلها. واحدة من أكثر المشاكل شيوعاً هي الإنشاء المفرط للسلاسل النصية.
\n\nالمشكلة: دمج السلاسل النصية في حلقة
\nالسلاسل النصية في C# غير قابلة للتغيير. كل عملية + تنشئ سلسلة نصية جديدة، مما يؤدي إلى تخصيصات مستمرة وضغط على GC:
string result = "";\nfor (int i = 0; i < 10000; i++)\n{\n result += i.ToString(); // سيء: يتم إنشاء 10000 سلسلة نصية جديدة\n}\n\nالحل: StringBuilder
\nتعمل الفئة StringBuilder مع مخزن مؤقت للأحرف وتقلل من التخصيصات:
var sb = new StringBuilder();\nfor (int i = 0; i < 10000; i++)\n{\n sb.Append(i);\n}\nstring result = sb.ToString(); // جيد: تخصيص واحد في النهاية\n\nتجنب التغليف (Boxing)
\nالتغليف (تعبئة الأنواع القيمية في كائن) هو مصدر آخر للتخصيصات المخفية. مثال:
\nArrayList list = new ArrayList();\nlist.Add(42); // تغليف: يتم تعبئة int في object\n\n// الأفضل استخدام المجموعات العامة:\nList<int> list2 = new List<int>();\nlist2.Add(42); // بدون تغليف\n\n3. تحسين استعلامات LINQ
\nLINQ أداة قوية، لكن استخدامها غير الصحيح يمكن أن يبطئ التطبيق بشكل كبير. المشكلة الرئيسية هي التكرارات المتعددة للمجموعات.
\n\nالمشكلة: التكرارات المتعددة
\nكل استدعاء لـ Where() أو Select() أو Count() يمكن أن يعيد تكرار المجموعة من جديد إذا لم يتم تجسيد النتيجة:
var numbers = Enumerable.Range(1, 1000000);\nvar evenNumbers = numbers.Where(n => n % 2 == 0);\nint coun