LINQ to XML - Part 2

يحوي الدروس و الملخصات التي قام أعضاء الموقع بكتابتها .

المشرف: Mohammad_807

LINQ to XML - Part 2

مشاركة غير مقروءةبواسطة HammooD » الأحد إبريل 04, 2010 10:28 pm

في الدرس السابق تحدثنا عن الميزات التي تقدمها لنا LINQ في التعامل مع الـ XML و الآن حان الوقت لنتحدث قليلاً عن LINQ و لكن قبل ذلك لا بد أن نأتي على بعض المفاهيم الأساسية فيها و الجديدة علينا و سنبدأ بـ Var Keyword :
اعتدنا فيما سبق عندما كنا نعرف المتحولات أن نضع نوع المتحول في البداية و من ثم نكتب اسم المتحول كما في المثال التالي :
CODE: تحديد الكل
int MyNumber;

يبدو واضحاً هنا أننا كنا نعلم نوع المتحول قبل أن نقوم بتعريفه و لكن ماذا إن لم نكن متأكدين من نوع المتحول ؟ ستبدو هذه الفرضية غريبة بعض الشيء ولكن قد يحصل في بعض الأحيان أن نقوم باستعلام ما و نحن غير واثقين من نوع المعطيات التي سيعيدها هذا الاستعلام فالحل الذي قدمته لنا LINQ هو أن نقوم بتعريف متحول باستخدام الكلمة var و هنا سيكون نوع المتحول مجهولاً و لن يتم تحديد النوع الحقيقي إلا أثناء الـ Compile time . مثال :
CODE: تحديد الكل
var text = "Hello LINQ!";

فهنا سيتم أثناء وقت الترجمة تحديد نوع المتحول على أنه string .
هناك بعض القيود على الكلمة المفتاحية var حيث لا يمكن كتابتها في جسم الصف مباشرة و إنما داخل Scope معين و لا يمكن كتابة تابع مجهول النوع و أيضأ فإن الوسطاء التي نمررها للتابع لا يمكن أن تكون مجهولة النوع ... الأمثلة التالية توضح الكلام السابق :
CODE: تحديد الكل
class Full_of_Errors
    {
        var s = 9;
        public var MyMethod()
        {
        }
        public void MyMethod2(var t)
        {
        }

    }

سنتكلم الآن عن أمر مختلف قليلاً و هو الـ Extension Methods :
قدمت لنا .Net 3.5 ميزة جديدة هي الـ Extension Methods حيث أننا قد نضطر في كثير من الأحيان إلى إضافة توابع لصف ما دون أن نغير في الكود الأصلي لهذا الصف ..يحدث هذا كثيراً عندما لا تكون لدينا صلاحية الوصول إلى الصف الذي نريد إضافة تابع له فالحل هنا هو كتابة Extension Method لهذا الصف .
إن هذه التوابع هي توابع static داخل صفوف static و كمثال عليها لنأخذ الصف التالي :
CODE: تحديد الكل
public static class StringExtensions
    {
        public static int ToInt(this string number)
        {
            return Int32.Parse(number);
        }
        public static string DoubleToDollars(this double number)
        {
            return string.Format("{0:c}", number);
        }
        public static string IntToDollars(this int number)
        {
            return string.Format("{0:c}", number);
        }
        public static string HammooD(this float number)
        {
            return string.Format("{0:c}", number);
        }
    }

لاحظوا استخدام this في جميع التوابع السابقة و لكن الجدير بالذكر هنا أننا قد استخدمناها كمتحول و ليس كمؤشر على الكائن الحالي كما اعتدنا من قبل .. في الحقيقة فإن الهدف من استخدامها هنا هو أن نعلم الكومبايلر أن هذا التابع هو Extension Method و ليس مجرد تابع static ..ربما لم ترد مايكروسوفت إيجاد كلمة مفتاحية جديدة من أجل التصريح عن الـ Extensions فاكتفت بكلمة موجودة مسبقاً هي this . مع العلم أنه في VB.Net يجب استخدام الصفة ExtensionAttribute عند التصريح عن Extension Method .
لنقم الآن بتعريف متحول من النوع int في التابع Main و نلاحظ الخيارات التي ستظهر في الإتمام التلقائي :
f1.png

أما في حالة تعريف متحول من النوع double :
f2.png

لاحظ هنا كيف تم تمييز التابع المطلوب عن طريق نوع المتحول .

لننتقل الآن إلى أمر آخر و هو Lambda Expression :
لتكن لدينا مصفوفة الأسماء التالية :
CODE: تحديد الكل
string[] names2 = new string[] { "Hammod", "Tarek", "Alyan", "Nawwar" };

نريد أن نبحث عن اسم ما يبدأ بالحرف A في هذه المصفوفة لذلك لنقم بكتابة التعليمة التالية :
CODE: تحديد الكل
var name2 = names2.Select(s => s.StartsWith("A"));

(طبعا لاحظوا وجود name2 و names2) .. ما الذي حصل ؟؟!
أكثر شيء قد تلاحظونه في التعليمة السابقة هو إشارة <= الغريبة و هي ما نسميه هنا بتعبير Lambda . لن ندخل في التفاصيل كثيراً فما يهمنا معرفته هنا أن Lambda تقوم بتطبيق تابع على متحول ما حيث يكون المتحول موجوداً في القسم اليساري من التعبير و يكون التابع موجوداً في القسم الآخر .
في مثالنا السابق نريد أن نختبر الأسماء الموجودة في المصفوفة names2 و نبحث عن الاسم الذي يبدأ بالحرف A فما سنقوم به هو أن نستعمل التابع Select (الذي هو Extension) و نقوم بتمرير الأسماء واحداً تلو الآخر (لسنا هنا بحاجة إلى حلقة فالتابع سيكرر نفسه من أجل جميع العناصر) عن طريق المتحول s إلى التابع StartWith الذي يختار المطلوب منها.
لاحظوا أننا لم نكن بحاجة إلى تحديد نوع المتحول s لأنه سيتحدد من نوع المعطيات الموجود في المصفوفة names2 و أيضاً فإننا استعملنا var في تعريف المتحول name2 الذي سنضع به النتيجة النهائية و سيكون نوعه وقت الترجمة هو :
System.Collections.Generic.IEnumerable<bool>

قد تتساءلون الآن لماذا أصبح نوع المتحول name2 هكذا ؟ الإجابة أن التابع StartWith يرد قيمة منطقية True أو False لكل اسم في المصفوفة بحسب تحقق الشرط أو لا لذلك سيصبح المتحول name2 عبارة عن ما يشبه مصفوفة bool و في حال حاولنا طباعتها ستكون النتيجة كما يلي :
CODE: تحديد الكل
 string[] names2 = new string[] { "Hammod", "Tarek", "Alyan", "Nawwar" };
  var name2 = names2.Select(s => s.StartsWith("A"));
  foreach (bool b in name2)
  {
      Console.WriteLine(b);
  }

و الخرج :
CODE: تحديد الكل
False
False
True
False

الآن لنتحدث قليلاً عن Select و التي قلنا قبل قليل أنها Extension Method...
في حال أردنا رؤية تعريف هذا التابع (عن طريق وضع مؤشر الكتابة على اسم التابع و ضغط مفتاح F12) سنجد التعريف الغريب التالي :
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);

حسناً من الواضح أن TResult هي النوع الذي سيعيده استدعاء هذا التابع (و هو في حالتنا السابقة bool ) و أن TSource هو نوع المتحولات التي نمررها للتابع (في حالتنا السابقة string ) و نلاحظ أيضاً وجود this (كما سبق و تحدثنا عن الـ Extensions ) .
أما بالنسبة لوسطاء هذا التابع سنجد في البداية source الذي يعبر عن المصفوفة التي قمنا بإدخالها و سنجد أيضاً ما يسمى بـ Func و هو في الحقيقة السبب الذي جعلني أدخل في تفاصيل التابع Select .
إذا ما هذا الـ Func ؟
- كنا نعلم مسبقاً وجود مؤشرات على توابع و تسمى هذه المؤشرات بالـ Delegates (يوجد شروحات كثيرة عنها لمن لم يتعامل معها مسبقاً) ولكن باختصار شديد فإننا بدل أن نستدعي تابعا ما فإننا نقوم باستدعاء Delegate خاص به و تمرير القيمة المناسبة إلى هذا الـ Delegate و هذا ما يتم استخدامه عند التعامل مع الأحداث (Events)التي سبق و تعاملتم معها بالتأكيد ...كمثال صغير لمن لم يتعامل معها من قبل :
-
CODE: تحديد الكل
public delegate string MyDelegate(int index);
public static string ReturnName(int index)
{
  string[] names3 = new string[] { "Hammod", "Tarek", "Alyan", "Nawwar" };
  return names3[index];
}
static MyDelegate SDelegate = new MyDelegate(ReturnName);


- في البداية قمنا بتعريف Delegate يرد قيمة string و يأخذ قيمة int (هذا يعني أنه يجب تطبيقه على توابع مشابهة له تماماً) و من ثم قمنا بتعريف تابع عادي يقوم برد اسم في مصفوفة ما حسب الـ index .
- بعدها قمنا بإنشاء مؤشر (Delegate) على التابع السابق (لاحظ كيفية تمرير اسم التابع المطلوب في باني الـ Delegate ) . هنا لم يبق لنا إلا استدعاء هذا المؤشر في التابع Main لنشاهد النتيجة المطلوبة :
-
CODE: تحديد الكل
Console.WriteLine(SDelegate(1));

و النتيجة وضوحا هي Tarek .

أنصح بإعادة قراءة الفقرة السابقة بتمعن أكثر من مرة لتثبيت فكرة الـ Delegate و هناك شروحات كثيرة لمن أراد التوسع فيها .
حسناً كنا نتكلم عن Func أليس كذلك ؟ أصبح بإمكاننا الآن بكل بساطة أن نقول أن Func هو تابع يستخدم لتمثيل Delegate يرد قيمة ما أي :

CODE: تحديد الكل
Func<TSource, TResult>

و القيمة المعادة هي TResult .
- هناك نوع آخر مشابه لـ Func يسمى Action يستخدم لتمثيل Delegate لا يرد قيمة معينة .
الآن إذا عدنا إلى تعريف التابع Select سنجد Func اسمه selector و هو التابع الذي سنقوم بتطبيقه على جميع الأسماء في المصفوفة (selector كما ذكرنا هو مؤشر لتابع و نقوم باستدعاء هذا التابع عن طريق هذا المؤشر)...
أعتقد أن الصورة بدأت تتوضح قليلاً و لكن كما ذكرت قبل قليل يفضل إعادة قراءة هذه الفقرات أكثر من مرة مع التطبيق.
الآن أصبح بإمكاننا كتابة الاستعلام السابق بواسطة Func كما يلي :
CODE: تحديد الكل
Func<string, bool> aOnly = delegate(string s) { return s.StartsWith("A");};
string[] names3 = new string[] { "Hammod", "Tarek", "Alyan", "Nawwar" };
var name3 = names3.Select(aOnly);


- ستلاحظون أننا قمنا بكتابة جسم لتابع بعد تعريف الـ dalegate بدون أن نعرف تابعاً! ، هذه الميزة موجودة في C# و كما قلنا فإن الـ Delegate هو مؤشر لتابع لذلك قمنا بكتابة جسم التابع مباشرة بعد تعريف هذا المؤشر ...طبعا كان بإمكاننا كتابة تابع مستقل و النتيجة نفسها في الحالتين.
- قد يتساءل البعض ماذا لو كنا نريد إعادة القيمة التي تطابق الشرط (الحرف الأول من الاسم هو A )؟ ، الحل هو أن نستخدم التابع Single بدلاً من Select و الذي يرد لنا الاسم المطلوب من المصفوفة على الشكل التالي :

CODE: تحديد الكل
Console.WriteLine(names3.Single(aOnly));

و الخرج :

Alyan


من المهم جداً أن نعلم أن Single تستخدم للاستعلامات التي نتيجتها هي قيمة وحيدة فقط ..مثلا في مصفوفة الأسماء الأسماء السابقة لو كان هناك أكثر من اسم يبدأ بالحرف A و استخدمنا التعليمة Single سيحدث Exception أثناء التشغيل بسبب وجود أكثر من قيمة. سيحصل Exception أيضاً إن استعملنا Single و لم يكن هناك أي قيمة مطابقة للشرط في مصفوفة الأسماء.

بقي لنا أن نتحدث عن بعض أشكال الاستعلامات في LINQ و التي تتلخص في الأمثلة التالية :
CODE: تحديد الكل
string[] camps = new string[] { "CodeCamp2007", "CodeCamp2008", "CodeCamp2010" };
var currentCamp = from camp in camps
                  where camp.EndsWith(DateTime.Now.Year.ToString())
                  select camp;


ستلاحظون تشابهاً بين الكود السابق و بين تعليمات SQL باستثناء أن تعليمة Select تأتي في نهاية الاستعلام.


يمكننا أيضاً كتابة الاستعلامات بالأشكال التالية و جميعها سيعطي نفس النتيجة :
CODE: تحديد الكل
string currentCamp2 = camps.Where(c => c.EndsWith(DateTime.Now.Year.ToString())).Single();

string currentCamp3 = camps.Single(c => c.EndsWith(DateTime.Now.Year.ToString()));

string currentCamp4 = camps.Select(c => c).Where(c => c.EndsWith(DateTime.Now.Year.ToString())).Single();




كان هذا الجزء من LINQ to XML مليئاً بالمفاهيم الجديدة و الأساسية و أعتقد أن أغلبها كان جديداً بالنسبة للكثيرين و ربما سيبدو أكثر صعوبة من القسم الأول و الشرح هنا هو شرح مختصر لأنه لا يلزمنا الآن التعمق كثيراً في هذه المفاهيم، لم نتطرق للـ XML هنا بعكس الجزء السابق...في المرة القادمة إن شاء الله سنكمل في هذه السلسة و نعود للحديث عن XML بشكل أوسع .


Happy Programming!
-----------------------------------------------------------------------------
الشرح بصيغة PDF :
LINQ2XML Part2 By HammooD.rar
A man who won't die for something is not fit to live
صورة العضو الشخصية
HammooD
متميز برمجة عام
متميز برمجة عام
 
مشاركات: 2199
اشترك في: الأربعاء مارس 05, 2008 4:12 pm
الجتس: ذكر
الشهادة الثانوية: سورية
الجامعة: جامعة دمشق
الكلية: الهندسة المعلوماتية
المرحلة الدراسية: السنة الخامسة
الاختصاص: شبكات و نظم

العودة إلى دروس في البرمجة

الموجودون الآن

المستخدمون المتصفحون لهذا المنتدى: لا يوجد أعضاء مسجلين متصلين و 1 زائر

cron