صراع العوالم: using داخل أم خارج namespace في C#
\n\nإذا كنت قد كتبت بلغة C# ولو مرة واحدة، فمن المحتمل أنك رأيت هذا الجدال في التعليقات أو على Stack Overflow. بعض المطورين يصرون على كتابة using داخل namespace، بينما يضعها آخرون خارجه. وكل منهم مقتنع بأن أسلوبه هو الصحيح الوحيد. لكن من منهم على صواب؟ والأهم من ذلك، هل يؤثر هذا على أداء الكود الخاص بك؟
دعونا نناقش هذا السؤال مرة وإلى الأبد، ونلقي نظرة تحت غطاء المحرك للمترجم (compiler) ونعرف ما يحدث حقًا لتوجيهات using الخاصة بك. المفسد: الإجابة ستفاجئك.
ما هي توجيهات using وما فائدتها؟
\n\nقبل أن نغوص في التفاصيل، دعونا نتذكر الأساسيات. توجيهات using هي تذكرة دخولك إلى عالم مساحات الأسماء (namespaces) الخاصة بالآخرين. بدونها، سيتعين عليك كتابة الأسماء المؤهلة بالكامل في كل مرة:
System.Console.WriteLine("مرحبًا بالعالم!");\nSystem.Collections.Generic.List<int> numbers = new System.Collections.Generic.List<int>();\n\nأوافق، يبدو هذا فظيعًا. توجيهات using تجعل الكود أنظف وأكثر قابلية للقراءة:
using System;\nusing System.Collections.Generic;\n\nConsole.WriteLine("مرحبًا بالعالم!");\nList<int> numbers = new List<int>();\n\nلكن أين نضعها - هذا هو السؤال الذي يعذب أجيالًا من مطوري C#.
\n\nمعسكران: خارج vs داخل
\n\nإليك كيف يبدو الأسلوبان:
\n\nالخيار 1: using خارج namespace
\n\nusing System;\nusing System.Collections.Generic;\n\nnamespace MyApp\n{\n class Program\n {\n static void Main()\n {\n List<int> list = new List<int>();\n }\n }\n}\n\nالخيار 2: using داخل namespace
\n\nnamespace MyApp\n{\n using System;\n using System.Collections.Generic;\n\n class Program\n {\n static void Main()\n {\n List<int> list = new List<int>();\n }\n }\n}\n\nللوهلة الأولى، الفرق هو فقط في المسافات البادئة. لكن المجتمع انقسم إلى معسكرين لا يمكن التوفيق بينهما. دعونا نناقش حجج كل منهما.
\n\nالحجة رقم 1: الأداء والترجمة
\n\nالسؤال الأكثر شيوعًا: هل يؤثر موقع using على الأداء؟ الإجابة المختصرة: لا. وإليك السبب.
توجيهات using هي مجرد سكر نحوي (syntactic sugar) للمترجم. إنها موجودة فقط في مرحلة الترجمة. عندما يتحول الكود الخاص بك إلى IL (Intermediate Language)، لا يبقى أي أثر لتوجيهات using. يستخدمها المترجم فقط لحل أسماء الأنواع (types)، ثم يتخلص منها.
دعونا نثبت هذا عمليًا. تأمل الكود التالي:
\n\n// الملف: Program.cs\nusing System;\nusing System.Collections.Generic;\n\nnamespace MyApp\n{\n class Program\n {\n static void Main()\n {\n List<int> list = new List<int>();\n Console.WriteLine(list.Count);\n }\n }\n}\n\nبعد الترجمة إلى كود IL، سترى شيئًا كهذا (مبسط جدًا):
\n\n.method private hidebysig static void Main() cil managed\n{\n .entrypoint\n .maxstack 1\n .locals init (\n [0] class [System.Runtime]System.Collections.Generic.List`1<int32> list\n )\n \n // إنشاء List<int>\n newobj instance void class [System.Runtime]System.Collections.Generic.List`1<int32>::.ctor()\n stloc.0\n \n // استدعاء Console.WriteLine\n ldloc.0\n callvirt instance int32 class [System.Runtime]System.Collections.Generic.List`1<int32>::get_Count()\n call void [System.Console]System.Console::WriteLine(int32)\n \n ret\n}\n\nلاحظ: في كود IL، يتم استخدام الأسماء المؤهلة بالكامل (System.Collections.Generic.List`1,