استفاده از روش متدی

LINQ مجموعه‌ای از متدهای توسعه یافته هست که، به رابط IEnumerable<T> الحاق شده‌اند. این متدها در فضای نامی System.Linq وجود دارند و عضوی از کلاس استاتیک Enumerable هستند. قبلاً یاد گرفته‌اید که متدهای توسعه یافته نوع خاصی از متدها هستند که، برای توسعه کلاس‌هایی که به سورس آن‌ها دسترسی ندارید به وجود آمده‌اند. به عنوان مثال شما می‌توانید متدی به نام ToTitleCase() را به کلاس System.String الحاق کنید که با صدا زدن آن به وسیله یک رشته، حروف اول کلمات تشکیل دهنده آن (رشته) به حالت بزرگ نمایش داده شوند. اجازه دهید در مورد متد Select<TSource,TResult>() از فضای نامی System.Linq توضیح دهیم. همانطور که در زیر می‌بینید، این متد به رابط IEnumerable<T> الحاق شده است.

public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source, Func<TSource, TResult> selector)

اولین پارامتر از یک متد توسعه یافته، مشخص کننده نوعی است که توسعه می‌دهد. با کلمه کلیدی this شروع و بعد از آن به ترتیب نوع و نام نمونه متغیر قرار می‌گیرد. همانطور که می‌بینید نوع خروجی متد بالا IEnumerable<T> است. این به شما اجازه می‌دهد که متدها را به صورت زنجیره‌ای فراخوانی کنید. همانطور که در درس‌های قبلی گفته شد، برای فراخوانی متدهای LINQ شما باید از عبارات لامبدا استفاده کنید. گرچه شما می‌توانید به جای عبارات لامبدا از متدهای بی نام هم استفاده کنید ولی استفاده از عبارات لامبدا کوتاه‌تر، ساده‌تر است و کد شما را خواناتر می‌کند. در این درس فرض شده است که خواننده اطلاعات خوبی از عبارات لامبدا دارد. در ادامه شما با روش جدیدی برای پرس و جو از منبع داده آشنا می‌شوید و آن استفاده از شکل متدی LINQ است. در این روش شما فقط باید متد مورد نظر خود را صدا زده و اطلاعات مورد نظر خود را صرف نظر از الگوریتم آن بدست آورید. .Net Framework شامل نماینده‌هایی (delegate) است که می‌توانند متدهایی با تعداد پارامترهای مختلف و نوع خروجی متفاوت در خود نگه دارند. اگر به تعریف متد Select دقت کنید می‌بینید که پارامتر دوم از این متد شامل نماینده‌ی عمومی با نوع Func<TSource,TResult> است. نماینده می‌تواند متدهایی که پارامتر اول آنها از نوع TSource و نوع خروجی آنها TResult است را قبول کند. به عنوان مثال، Func<string,int> می‌تواند متدی که یک پارامتر از نوع string و خروجی از نوع int دارد را بپذیرد. در زیر لیست نماینده‌هایی را مشاهده می‌کنید که می‌توانید بسته به تعداد پارامترهای متد خود از آنها استفاده کنید.

نماینده توضیح
Func<T1, TResult> متدی با یک پارامتر و یک خروجی را در خود ذخیره می‌کند.
Func<T1, T2, TResult> متدی با 2 پارامتر و یک خروجی در خود ذخیره می‌کند.
Func<T1,T2,T3,TResult> متدی با 3 پارامتر ورودی و یک خروجی در خود ذخیره می‌کند.
Func<T1,T2,T3,T4, TResult> متدی با 4 پارامتر ورودی و یک خروجی در خود ذخیره می‌کند

با توجه به الگوی موجود در جدول بالا می‌توانید شکل نماینده برای متدهای با تعداد پارامتر بیشتر را حدس بزنید. این الگو را می‌توان تا سقف 16 پارامتر ادامه داد. اگر دوباره به شکل کلی متد Select() نگاهی بیندازید می‌بینید که دومین پارامتر آن ارجاع به متدی دارد که دارای یک پارامتر و یک مقدار برگشتی است. اجازه دهید مثالی را بررسی کنیم که نحوه‌ی استفاده از متد Select() با عبارات لامبدا را مشخص می‌کند.

int[] numbers = { 1, 2, 3, 4, 5 };

var result = numbers.Select(n => n);

foreach(var n in result)
{
    Console.Write(n + " ");
}
1 2 3 4 5

این نکته را یادآور می‌شویم که اولین پارامتر یک متد الحاقی در واقع یک پارامتر نیست بلکه مشخص کننده این است که قصد توسعه کدام نوع را داریم. در نتیجه دومین پارامتر که یک عبارت لامبدا را می‌پذیرد تنها پارامتری است که باید در متد Select() مشخص کنید. در متد Select()، از عبارت لامبدایی استفاده شده است که یک پارامتر صحیح را دریافت می‌کند و خروجی از نوع صحیح را بر می‌گرداند. اگر شما با عبارات لامبدا آشنایی نداشته باشید عبارت موجود در مثال بالا برای شما عجیب است. وظیفه‌ای که عبارت لامبدا در مثال بالا دارد این است که هر عدد موجود در منبع داده را به نتیجه پرس و جو اضافه می‌کند. به این نکته توجه کنید که پرس و جو نتایج را بدون تغییر آنها بر می‌گرداند. اجازه دهید عبارت لامبدا در مثال قبلی را به شکل مفیدتری تغییر دهیم.

int[] numbers = { 1, 2, 3, 4, 5 };

var result = numbers.Select(n => n + 1);

foreach(var n in result)
{
    Console.Write(n + " ");
}
2 3 4 5 6

عبارت لامبدا در مثال بالا هر عدد موجود در منبع داده را بدست آورده سپس یک واحد به آن اضافه می‌کند و در آخر آن را به نتیجه پرس و جو اضافه می‌کند. بیشتر متدهای موجود در فضای نامی System.Linq خروجی از نوع IEnumerable<T> دارند. این ویژگی به شما این امکان را می‌دهد که متدها را به صورت زنجیره‌ای فراخوانی کنید. به عنوان مثال، کد زیر را صرف نظر از متدهای جدید آن در نظر بگیرید. نکته مهمی که در خط زیر وجود دارد فراخوانی زنجیره‌ای متدهای LINQ است.

var result = numbers.Where(n => n > 3).OrderBy(n => n).Select(n => n);

در این درس به شما نحوه‌ی استفاده از متد Select() آموزش داده شد. متدهای زیادی باقی مانده است که در درس‌های بعدی به آن پرداخته می‌شود.
حالا شما می‌توانید نتایج بهتری را با استفاده از متد Select() بدست آورید، ولی نکته‌ی مهم این است که حالا شما می‌توانید از شکل متدی LINQ نیز برای پرس و جو از منبع داده استفاده کنید. در واقع کامپایلر برای پرس و جو از منبع داده از شکل متدی استفاده می‌کند. استفاده از عبارات پرس و جو برای پرس و جو از منبع داده فقط لایه‌ای برای فراخوانی ساده‌تر متدهای الحاقی است. به عنوان نمونه، عبارت پرس و جوی زیر را در نظر بگیرید :

var result = from p in persons
             where p.Age >= 18
             order by p.Name
             select p.FirstName + " " + p.LastName;

در زمان کامپایل عبارت پرس و جوی بالا به دنباله‌ای از متدهای توسعه یافته که در فضای نامی System.Linq قرار دارند تبدیل می‌شود. کد واقعی که کامپایلر از آن برای پرس و جو از منبع داده استفاده می‌کند به شکل زیر است :

var result = persons.Where(p => p.Age >= 18)
                    .OrderBy(p => p.Name)
                    .Select(p => p.FirstName + " " + p.LastName);

همانطور که می‌بینید متدها به صورت زنجیره‌ای فراخوانی شده‌اند. این به این دلیل است که هر کدام از متدها خروجی از نوع IEnumerable<T> دارند. بعد از فراخوانی متد Where()، از متد OrderBy() بر روی خروجی داده‌ها و سپس بر روی مجموعه جدید از متد Select() استفاده کرده‌ایم.

سؤال این است که باید از کدام شکل استفاده کنیم؟ توصیه می‌شود از عبارات پرس و جو استفاده کنید زیرا نسبت به شکل متدی ساده‌تر و خوانایی بالاتری دارند. اما بهتر است با شکل متدی هم آشنایی داشته باشید زیرا تنها راهی است که CLR با استفاده از آن پرس و جو ها را انجام می‌دهد.