مقدمه

در درس قبل، با اجزای سازنده‌ی زبان C# که تکنولوژی LINQ را ممکن می‌سازند، آشنا شدیم. اکنون زمان آن است که این مفاهیم را به صورت عملی به کار بگیریم. از آنجایی که آرایه‌ها در C# اینترفیس IEnumerable<T> را پیاده‌سازی می‌کنند، آن‌ها یکی از اولین و ساده‌ترین منابع داده‌ای هستند که می‌توانیم با استفاده از LINQ روی آن‌ها کوئری بزنیم. در این درس، یاد می‌گیریم که چگونه با استفاده از هر دو سینتکس کوئری (Query Syntax) و سینتکس متد (Method Syntax)، داده‌ها را از آرایه‌ها فیلتر، مرتب و پرتاب (project) کنیم.

آماده‌سازی مثال

برای شروع، بیایید دو آرایه‌ی ساده به عنوان منبع داده‌ی خود تعریف کنیم. در طول این درس از این آرایه‌ها برای نمایش عملگرهای مختلف LINQ استفاده خواهیم کرد.

Copy Icon Program.cs
int[] numbers = { 5, 10, 8, 3, 6, 12, 9, 7 };
string[] names = { "Ali", "Sara", "Reza", "Mina", "Babak" };

فیلتر کردن داده‌ها با where

یکی از رایج‌ترین کارها در کوئری زدن، فیلتر کردن است؛ یعنی انتخاب عناصری که یک شرط خاص را برآورده می‌کنند. در LINQ، این کار با استفاده از عملگر where انجام می‌شود.

سینتکس کوئری (Query Syntax)

این سینتکس شبیه به SQL است و برای بسیاری از افراد، خوانایی بالاتری دارد.

Copy Icon Program.cs
// Find all numbers greater than 7.
var queryResult = from n in numbers
                    where n > 7
                    select n;

// The query is NOT executed until we iterate over it.
Console.WriteLine("Numbers greater than 7:");
foreach (var number in queryResult)
{
    Console.Write($"{number} "); // Output: 10 8 12 9
}

سینتکس متد (Method Syntax)

در این سینتکس، ما از متد بسطی Where به همراه یک عبارت لامبدا برای تعریف شرط فیلتر استفاده می‌کنیم.

Copy Icon Program.cs
// Find all names that contain the letter 'a'.
var methodResult = names.Where(name => name.Contains("a"));

Console.WriteLine("\nNames containing 'a':");
foreach (var name in methodResult)
{
    Console.Write($"{name} "); // Output: Sara Mina Babak
}

نکته مهم: هر دو سینتکس در نهایت توسط کامپایلر به یک کد مشابه تبدیل می‌شوند و از نظر عملکردی تفاوتی ندارند. انتخاب بین آن‌ها بیشتر به سلیقه و خوانایی در سناریوی مورد نظر بستگی دارد.

مرتب‌سازی داده‌ها با orderby

برای مرتب کردن نتایج کوئری، از عملگر orderby (در سینتکس کوئری) یا متدهای OrderBy و OrderByDescending (در سینتکس متد) استفاده می‌کنیم.

Copy Icon Program.cs
// Query Syntax: Sort numbers in descending order.
var sortedNumbersQuery = from n in numbers
                           orderby n descending
                           select n;
Console.WriteLine("\nNumbers sorted descending:");
Console.WriteLine(string.Join(", ", sortedNumbersQuery));

// Method Syntax: Sort names alphabetically.
var sortedNamesMethod = names.OrderBy(name => name);
Console.WriteLine("\nNames sorted alphabetically:");
Console.WriteLine(string.Join(", ", sortedNamesMethod));

پرتاب کردن داده‌ها با select

اغلب اوقات، ما نمی‌خواهیم خود عناصر اصلی را برگردانیم، بلکه می‌خواهیم آن‌ها را به یک شکل جدید پرتاب (Project) کنیم. برای مثال، ممکن است بخواهیم از یک آرایه‌ی عددی، مربع هر عدد را برگردانیم، یا از یک آرایه‌ی رشته‌ای، طول هر رشته را. این کار با عملگر select انجام می‌شود.

Copy Icon Program.cs
// Using 'select' to transform the data.
// Here we create an anonymous type for each number.
var projectedNumbers = from n in numbers
                         select new { Original = n, Square = n * n };

Console.WriteLine("\nProjected numbers:");
foreach (var item in projectedNumbers)
{
    Console.WriteLine(item);
}

خروجی این کد، لیستی از اشیاء ناشناس خواهد بود:

Projected numbers:
{ Original = 5, Square = 25 }
{ Original = 10, Square = 100 }
...
                    

اجرای تأخیری (Deferred Execution)

یکی از مهم‌ترین مفاهیم در LINQ، اجرای تأخیری است. وقتی شما یک کوئری LINQ را تعریف می‌کنید (مانند var queryResult = ... آن کوئری در همان لحظه اجرا نمی‌شود. در واقع، شما فقط "دستورالعمل" یا "نقشه‌ی" کوئری را ساخته‌اید. کوئری تنها زمانی واقعاً اجرا می‌شود که شما شروع به پیمایش نتایج آن کنید، معمولاً با یک حلقه‌ی foreach یا با فراخوانی متدهایی مانند ToList()، ToArray()، First() یا Count(). این ویژگی به LINQ اجازه می‌دهد تا کوئری‌ها را بهینه کرده و چندین عملیات را در یک مرحله‌ی واحد ترکیب کند.