السبت، 28 مايو، 2011

ما يجب أن تتجنبه عند معالجة السلاسل النصية على منصة الدوت نت

على عكس بعض اللغات الكلاسيكية (مثل السي و السي++ ..الخ)  فإن السلاسل النصية في منصة الدوت نت ليست مجرد جداول من الرموز (Chars) بل هي في الحقيقة عبارة عن كائنات من الصنف String و هي فئة غنية بالدوال التي ستجعل التعامل معها أكثر سهولة من أي وقت مضى و ستغنيك عن كثير من العمليات عديمة الجدوى التي كنت ستضطر للقيام بها لو كنت تبرمج بلغة أخرى، لكن اذا أزلنا الستار عن هذه الفئة سنجد أنها في الحقيقة تقوم بإدارة جدول من الرموز (التي تستعمل ترميز Unicode 16) تتولى توليده تلقائياً كما تقوم بكل العمليات اللازمة من أجل تعديله متى دعت الحاجة (بما في ذلك تحرير الذاكرة بالنسبة للرموز التي لم تعد لنا بها حاجة)، أي أن متغيراً نصياً من 3 رموز لن يقوم بحجز نفس السعة التي سيحجزها متغير من 10 رموز على عكس السي مثلاً حيث ستكون ملزماً بالاحتفاظ بباقي الخانات رغم أنك لا تستعملها (جميل، أليس كذلك؟)..

أمر رائع آخر أن منصة الدوت نت تعامل الفئة String كأنها نوع Type و بذلك فانه ليس من الضروري أن تقوم بكتابة new عند تعريف متغير جديد، لاحظ أنني قلت متغير و ليس كائن كونه من الممكن اسناد قيمة له مباشرة بالمعامل = كما أنه من الممكن اضافة أحرف إلى آخر النص باستعمال المعامل += و إليك المثال التالي:

string s = "mobarmij"; // تعريف متغير نصي
s += ".com"; // s = "mobarmij.com"

مع أن هذا الأمر يبدو عملياً جداً إلا أنه في الحقيقة يقوم بتقليل أداء برنامجك خصوصاً ان كانت النصوص كبيرة نوعاً ما أو ان كنت تقوم بعمليات متكررة عليها، و السبب في ذلك أنه عند كل مرة تقوم فيها بإضافة نص إلى متغير نصي تتم العملية بشكل غير مباشر حيث يتم انشاء متغير جديد يوضع فيه النص الناتج عن العملية ثم يتولى الـ Runtime تحويل المؤشر إليه و تحرير الذاكرة من المتغير القديم، توضحت الفكرة الآن؟ ليس بعد؟ إليك مثال بسيط يشرح الأمر: لنفترض أنني قمت بإنشاء متغير نصي اسمه s و أسندت إليه قيمة "mobarmij"  (متغير من 8 رموز)، الآن أريد إضافة بعض الحروف إلى النص السابق سأطبق الأمر التالي "s+= ".com ما سيحدث في الحقيقة أن الـ Runtime سينشأ متغيراً جديداً من 12 رمزاً و الذي سيسند إليه القيمة "mobarmij.com" بعد ذلك سيحول الاسم s ليشير إلى المتغير الجديد.

الحل هو فئة StringBuilder:
تمكننا هذه الفئة من اجراء العمليات على المتغيرات من النوع string بشكل مباشر مما يتيح لنا ضمان أداء عالي لبرامجنا و هذا رسم توضيحي من مكتبة msdn يبين فرق الأداء بين الطريقتين:


لاحظ كيف تزداد المدة الزمنية المخصصة للعملية مع كل مرة عند الاعتماد على الطريقة التقليدية أما مع فئة StringBuilder فالمدة اللازمة شبه ثابتة لذا ينصح باستعمالها في البرامج التي تعتمد بشكل أساسي على معالجة السلاسل النصية.

لا أريد جعل هذا الموضوع يتحدث عن الـ StringBuilder بشكل خاص لكن لمن أراد التعمق فيه فبإمكانه مراجعة هذا المقال من مكتبة msdn و أعدكم بموضوع يتناول هذه الفئة عما قريب هنا في المدونة.

نبقى في مجال أداء البرنامج و هذه المرة مع ممارسة أخرى أرى بعض المبرمجين يقومون بها و التي تخص مقارنة السلاسل النصية فهذا الكود مثلاً:
if (s1.Equals(s2))
يقوم الكود أعلاه بمقارنة المتغيرين كأنهما كائنان (Objects) و هذا أسرع بمرتين تقريباً من نظيره (الذي يقارن قيمتي المتغيرين):
if (s1 == s2)
و الذي بدوره أسرع بمقدار مرتين من:
if (String.Compare(s1, s2) == 0)

بهذا تكون تدوينتي قد شارفت على النهاية، أتمنى أن تكونوا قد استفدتم من هذه الأمثلة و اذا كان لديكم استفسار أو اثراء للموضوع فمرحباً بتعليقاتكم :).

دمتم بود..