اپراتورهای Element در LINQ

02 اردیبهشت 1398
درسنامه درس 8 از سری آموزش LINQ
LINQ-elements

سلام با یکی دیگر از آموزش های LINQ در خدمت شما عزیزان هستیم در این جلسه& آموزش اپراتورهای Element را بررسی خواهیم کرد.

Element Operators چیست؟

یکی از ساده ترین مبحث های LINQ، اپراتورهای Element هستند که کاربرد کلی آن ها برای بازگرداندن اولین یا آخرین عنصر یک مجموعه با روش های مختلف است

در ادامه با آن ها آشنا خواهیم شد.

اپراتورهای Element به شرح زیر هستند:

  1. First
  2. FirstOrDefault
  3. Last
  4. LastOrDefault
  5. ElementAt
  6. ElementAtOrDefault
  7. Single
  8. SingleOrDefault
  9. DefaultIfEmpty

که خلاصه ی کاربرد آن ها مطابق جدول زیر است:

کاربرد نام اپراتور
عنصر اول یک مجموعه را باز می گرداند First
کاملا مانند First است اما در صورت خالی بودن مجموعه مقدار 0 را باز می گرداند FirstOrDefault
عنصر آخر یک مجموعه را باز می گرداند Last
کاملا مانند Last است اما در صورت خالی بودن مجموعه مقدار 0 را باز می گرداند LastOrDefault
عنصر nام یک مجموعه را باز می گرداند ElementAt
کاملا مانند ElementAt است اما در صورت خالی بودن مجموعه مقدار 0 را باز می گرداند ElementAtOrDefault
عنصر خاصی را از مجموعه باز می گرداند Single
مانند Single اما در صورت خالی بودن مجموعه مقدار 0 را باز می گرداند SingleOrDefault
اگر مقادیر خالی یا صفر شامل لیست یا مجموعه باشد، مقادیر پیش فرض را برمی گرداند DefaultIfEmpty

متد First

این متد عنصر اول یک مجموعه را باز می گرداند اما اگر شرطی در آن قرار داده شود اولین عنصری که در شرط صدق می کند را باز می گرداند.

الگوی استفاده از آن به صورت زیر است:

var result = objList.First();

توجه کنید نوع داده ی result از نوع داده ی objList خواهد بود.

به مثال زیر توجه کنید:

using System;
using System.Linq;
namespace LINQExamples
{
   class Program
    {
        static void Main(string[] args)
        {
            int[] objList = { 1, 2, 3, 4, 5 };
            int result = objList.First();
            Console.WriteLine("Element from the List: {0}", result);
            Console.ReadLine();
        }
    }
}

در این مثال نوع داده ی آرایه از نوع int است پس وقتی شی objList متد First را فراخوانی می کند، جنس خروجی آن (result) از نوع int خواهد بود.

در این مورد متد First بدون پارامتر به کار برده شده در نتیجه عنصر اول آرایه را باز می گرداند. حالا فرض کنید در متد First شرطی را به صورت زیر وارد کنیم:

int result = objList.First(x =>x%2==0);

در این حالت اولین عنصری که زوج است به عنوان خروجی در نظر گرفته می شود که در این مورد عدد 2 خواهد بود.

به مثال دوم از کاربرد First توجه کنید:

using System;
using System.Collections.Generic;
using System.Linq;
namespace LINQExamples
{
   class Program
    {
        static void Main(string[] args)
        {
            List<Student> stu = new List<Student>
            {
                new Student(){ID=1,Name="Jack" },
                new Student(){ID=2,Name="Harry" },
                new Student(){ID=3,Name="Jacob" },
            };
            Student result = stu.First(x =>x.ID%2==0);
            Console.WriteLine("Element from the List: {0} ID={1}", result.Name,result.ID);
            Console.ReadLine();
        }
    }
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

در این مثال عناصر موجود در List از جنس کلاس Student هستند که ما قصد داریم اولین شخصی که ID آن زوج است را در خروجی نشان دهیم. با دقت بسیار به نوع داده ی result توجه کنید.

خروجی:

Element from the List: Harry ID=2

بازنویسی کد به روش Query به صورت زیر است:

 Student result = (from st in stu
select st).First(x => x.ID % 2 == 0);

متد FirstOrDefault

قبل از توضیح این متد، در IDE خود یکی از مثال های متد First را به گونه ای تغییر دهید که First بر روی یک List یا آرایه ی خالی از عنصر اعمال شود. خواهید دید که با Run کردن، برنامه با exception مواجه می شوبد. راه حل این مشکل استفاده از متد FirstOrDefault است. در واقع کاربرد آن کاملا مشابه با First است حتی می توانید در دو مثال مربوط به First به جای آن از FirstOrDefault استفاده کنید. اما استفاده از این متد یک مزیت به همراه دارد که در صورت خالی بودن مجموعه، برنامه با exception مواجه نمی شود و مقدار پیش فرض صفر را باز می گرداند.

به مثال زیر توجه کنید:

using System;
using System.Linq;
namespace LINQExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] objList = { 1, 2, 3, 4, 5 };
            int[] objVals = { };
            int result = objList.FirstOrDefault();
            int val = objVals.FirstOrDefault();       
            Console.WriteLine("Element from the List1: {0}", result);
            Console.WriteLine("Element from the List2: {0}", val);
            Console.ReadLine();
        }
    }
}

کد بالا را اجرا کنید، خواهید دید که با این که objVals خالی از عنصر است اما برنامه بدون exception اجرا شده و مقدار پیش فرض صفر برای آرایه ی خالی در نظر گرفته می شود.

متد Last

از نظر کارکرد کاملا شبیه به متد First است با این تفاوت که متد First از اول یک مجموعه (سمت چپ) کار خود را شروع می کند اما متد Last از آخر یک مجموعه (سمت راست) آغاز به کار می کند.

الگوی استفاده از آن به صورت زیر است:

var result = objList.Last();

توجه داشته باشید که نوع داده ی var به نوع داده ی objList بستگی دارد.

به مثال زیر توجه کنید:

using System;
using System.Linq;
namespace LINQExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] objList = {1,2,3,4,5,6,7,8,9};
            int result = objList.LastOrDefault(x=>x%2==0);     
            Console.WriteLine("Element from the List1: {0}", result);
            Console.ReadLine();
        }
    }
}

خروجی آن عدد 8 خواهد بود زیرا متد Last از آخر آرایه شروع به پیمایش می کند و اولین عنصری که زوج است را باز می گرداند.

بازنویسی کد به روش Query به صورت زیر است:

 int result = (from x in objList
select x).Last(x =>x%2==0);

متد LastOrDefault

این متد نیز مانند FirstOrDefault عمل می کند اما جهت پیمایش مجموعه را از سمت راست شروع می کند و در صورت خالی بودن مجموعه عدد صفر را باز می گرداند.

کد زیر را در IDE خود بنویسید:

using System;
using System.Linq;
namespace LINQExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] objList = {1,3,5,7,9,13};
            int result = objList.Last(x=>x%2==0);
            Console.WriteLine("Element from the List1: {0}", result);
            Console.ReadLine();
        }
    }
}

حال آن را اجرا کنید، خواهید دید که با exception مواجه می شوید اما دلیل آن چیست؟

در این مورد شرط ما بازگرداندن آخرین عنصر زوج یک مجموعه است. همان طور که مشاهده می کنید تمامی عناصر موجود در آرایه فرد هستند پس با وجود شرط، آرایه ی موجود، یک آرایه ی خالی به حساب می آید. برای این که در اجرای کد بالا به exception برخورد نکنیم لازم است با استفاده از متد FirstOrDefault کد را باز نویسی کنیم تنها کافی است Last را به FirstOrDefault تغییر دهید.

روش Extension:

int result = objList.LastOrDefault(x=>x%2==0);

روش Query:

int result = (from x in objList
select x).LastOrDefault(x => x % 2 == 0);

با اعمال تغییر، دوباره کد را اجرا کنید. خواهید دید که این بار خروجی مقدار صفر خواهد یود.

متد ElementAt

کاربرد این متد بسیار ساده بوده و تنها ورودی آن پارامتری از جنس int است و با توجه به عدد وارد شده در آن، عنصری را به عنوان خروجی در نظر می گیرد.

الگوی استفاده از آن به صورت زیر است:

var result = objList.ElementAt(1);

توجه کنید که نوع داده ی var به نوع داده ی objList بستگی دارد.

مثال:

using System;
using System.Collections.Generic;
using System.Linq;
namespace LINQExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Student> stu = new List<Student>
            {
                new Student(){ID=1,Name="Jack" },
                new Student(){ID=2,Name="Harry" },
                new Student(){ID=3,Name="Jacob" },
            };
            Student result = stu.ElementAt(2);
            Console.WriteLine("Element from the List: {0} ID={1}", result.Name, result.ID);
            Console.ReadLine();
        }
    }
    public class Student
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }
}

در این مثال با استفاده ار متد ElementAt قصد داریم عنصر سوم از List را در خروجی چاپ کنیم. دقت به این نکته ضروری است که شمارنده ی متد ElementAt از  0 شروع می شود بنابراین در این مثال برای دستیابی به عنصر شماره سوم باید عدد 2 را در ElementAt وارد کنیم که در نتیجه ی آن خروجی به صورت

Element from the List: Jacob ID=3 خواهد شد.

برای پیاده سازی کد به روش Query به صورت عمل می کنیم:

 Student result = (from x in stu
select x).ElementAt(2);

به مثالی دیگر از کاربرد متد ElementAt توجه کنید:

using System;
using System.Linq;
namespace LINQExamples
{
class Program
{
static void Main(string[] args)
{
int[] objList = { 1, 2, 3, 4, 5 };
int result = objList.ElementAt(1);
int val = objList.ElementAt(3);
Console.WriteLine("Element At Index 1: {0}", result);
Console.WriteLine("Element At Index 3: {0}", val);
Console.ReadLine();
}
}
}

روش Query:

 int result = (from x in objList
select x).ElementAt(1);
 int val = (from x in objList
select x).ElementAt(3);
خروجی مثال دوم ElementAt
خروجی مثال دوم ElementAt

متد ElementAtOrDefault

این متد شباهت بسیار زیادی به متد ElementAt دارد با این تفاوت که اگر توسط یک مجموعه خالی از عضو فراخوانی شود، برنامه با exception مواجه نمی شود و مقدار پیش فرض را صفر در نظر می گیرد.

الگوی استفاده از آن به صورت زیر است:

var result = objList.ElementAtOrDefault(1);

توجه کنید که نوع داده ی var به نوع داده ی objList بستگی دارد.

مثال:

using System;
using System.Linq;
namespace LINQExamples
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] objList = {1,2,3,4,5};
            int result = objList.ElementAtOrDefault(1); 
            int val = objList.ElementAtOrDefault(3);
            int val1 = objList.ElementAtOrDefault(10);
            Console.WriteLine("Element At Index 1: {0}", result);
            Console.WriteLine("Element At Index 3: {0}", val);
            Console.WriteLine("Element At Index 10: {0}", val1);
            Console.ReadLine();
        }
    }
}

برنامه را اجرا و خروجی را مشاهده کنید. در مورد result و val، اعداد وارد شده در متد ElementAtOrDefault جز ایندکس آرایه objList هستند. پس عضو متناظر با آن در خروجی چاپ شده است. در مورد val1 که عضو یازدهم در خواست شده اما در آرایه، پنج عضو وجود دارد. چون از متد ElementAtOrDefault استفاده شده، روند اجرای برنامه با مشکل روبرو نخواهد شد و مقدار صفر را در خروجی چاپ می کند.

روش Query:

 int result = (from x in objList
               select x).ElementAtOrDefault(1);
 int val = (from x in objList
            select x).ElementAtOrDefault(3);
 int val1 = (from x in objList
             select x).ElementAtOrDefault(10);
خروجی مثال ElementAtOrDefault
خروجی مثال ElementAtOrDefault

متد Single

از این اپراتور برای بازگرداندن یک عنصر از مجموعه یا یک عنصر که در شرط صدق کند، استفاده می شود. در صورتی که بیش از یک عضو در مجموعه موجود باشد یا اصلا عضوی در مجموعه موجود نباشد خطای InvalidOperationException پرتاب می کند.

الگوی استفاده از آن به صورت زیر است:

var val = objList.Single();

توجه کنید که نوع داده ی var به نوع داده ی objList بستگی دارد.

برای درک بهتر این متد به مثال زیر توجه کنید:

using System;
using System.Linq;
using System.Collections.Generic;
namespace LINQExamples
{
      class Program
    {
        static void Main(string[] args)
        {
            List<Student> objStudent = new List<Student>()
            {
                new Student() { Name = "Suresh Dasari", Gender = "Male",Location="Chennai" },
                new Student() { Name = "Rohini Alavala", Gender = "Female", Location="Chennai" },
                new Student() { Name = "Praveen Alavala", Gender = "Male",Location="Bangalore" },
                new Student() { Name = "Sateesh Alavala", Gender = "Male", Location ="Vizag"},
                new Student() { Name = "Madhav Sai", Gender = "Male", Location="Nagpur"}
            };
            int[] objList = {2};
            var user = objStudent.Single(s => s.Name == "Suresh Dasari");
            string result = user.Name;
            int val = objList.Single();
            Console.WriteLine("Element from objStudent: {0}", result);
            Console.WriteLine("Element from objList: {0}", val);
            Console.ReadLine();
        }
    }
    class Student

    {
        public string Name { get; set; }
        public string Gender { get; set; }   
        public string Location { get; set; }
    }
}

در این مثال، ابتدا شی از لیستی که اعضای آن از جنس کلاس Student هستند، تعریف شده است. با استفاده از متد Single شرط یکتا بودن نام Suresh Dasari را در لیست چک کرده ایم که در صورت درست بودن شرط همان مقدار در خروجی چاپ می شود، در غیر این صورت خطای InvalidOperationException پرتاب می شود.

حالا به objList دقت کنید که یک آرایه از نوع int با یک عضو تعریف شده و در ادامه با استفاده از متد Single شرط تک عضوی بودن آرایه چک شده است. بنابراین مقدار عنصر موجود در آرایه به عنوان خروجی چاپ می شود. توجه داشته باشید خروجی برنامه خطای InvalidOperationException خواهد بود اگر یکی از حالت های زیر پیش آید:

حالت اول:

int[] objList = {2,5,6};

یا

int[] objList = {2,2};

یا

int[] objList = {};

حالت دوم:

List<Student> objStudent = new List<Student>()
            {
                new Student() { Name = "Suresh Dasari", Gender = "Male",Location="Chennai" },
                new Student() { Name = "Suresh Dasari", Gender = "Female", Location="Chennai" },
                new Student() { Name = "Suresh Dasari", Gender = "Male",Location="Bangalore" },
                new Student() { Name = "Sateesh Alavala", Gender = "Male", Location ="Vizag"},
                new Student() { Name = "Madhav Sai", Gender = "Male", Location="Nagpur"}
            };

یا

 List<Student> objStudent = new List<Student>(){};

برای بازنویسی کد به روش Query به این صورت عمل می کنیم:

 var user = (from stu in objStudent
             select stu).Single(s =>s.Name== "Suresh Dasari");
 int val = (from x in objList
            select x).Single();

متد SingleOrDefault

در مورد متد Single به یاد دارید که در دو حالت خالی بودن مجموعه و وجود بیش از یک عضو خطای InvalidOperationException  توسط این متد پرتاب می شود. در مورد متد SingleOrDefault کاملا به همین صورت است، فقط خالی بودن مجموعه از عضو، باعث بروز خطای InvalidOperationException نمی شود.

الگوی استفاده از متد SingleOrDefault به صورت زیر است:

var val = objList.SingleOrDefault();

توجه کنید که نوع داده ی var به نوع داده ی objList بستگی دارد.

مثال:

using System;
using System.Linq;
using System.Collections.Generic;
namespace LINQExamples
{
    class Program
    {
       static void Main(string[] args)
        {
            List<Student> objStudent = new List<Student>()
            {
                new Student() { Name = "Suresh Dasari", Gender = "Male",Location="Chennai" },
                new Student() { Name = "Rohini Alavala", Gender = "Female", Location="Chennai" },
                new Student() { Name = "Praveen Alavala", Gender = "Male",Location="Bangalore" },
                new Student() { Name = "Sateesh Alavala", Gender = "Male", Location ="Vizag"},
                new Student() { Name = "Madhav Sai", Gender = "Male", Location="Nagpur"}
            };

            int[] objList = { 1, 2, 3, 4, 5 };
            int[] objList2 = {};
            var user = objStudent.SingleOrDefault(i => i.Name == "Suresh Dasari");
            string result = user.Name;
            int val = objList.SingleOrDefault(j => j > 5);
            int val2 = objList2.SingleOrDefault();
            Console.WriteLine("Element from objStudent: {0}", result);
            Console.WriteLine("Element from objList: {0}", val);
            Console.WriteLine("Element from objList: {0}", val2);
            Console.ReadLine();
       }
    }
    class Student
    {
        public string Name { get; set; }
        public string Gender { get; set; }
        public string Location { get; set; }

    }

}

مشاهده می کنید که در آرایه ی objList عضوی بزرگ تر از 5 وجود ندارد در حالی که شرط قرار داده شده بر روی آن به صورت (SingleOrDefault(j => j > 5 است. در صورتی که از متد Single استفاده کنیم با خطا روبرو می شویم اما SingleOrDefault در صورت موجود نبودن عضو مورد نظر، خروجی صفر نمایش داده می شود.

در مورد آرایه ی objList2 هم به همین صورت است، متد SingleOrDefault بر روی یک آرایه ی خالی از عضو فراخوانی شده، پس خروجی آن صفر است.

روش Query:

 int val = (from x in objList
            select x).SingleOrDefault(j =>j>5);
 int val2 = (from x in objList2
             select x).SingleOrDefault();
خروجی مثال SingleOrDefault
خروجی مثال SingleOrDefault

متد DefaultIfEmpty

این متد اگر بر روی یک مجموعه ی عادی دارای عضو فراخوانی شود، به نوعی کار Select را انجام می دهد و تمامی عناصر توسط یک حلقه قابل دستیابی هستند اما اگر بر روی یک مجموعه ی خالی از عضو یا به طور کلی بر روی یک شی تهی فراخوانی شود، روند اجرای برنامه متفاوت خواهد بود که حالت های مختلف آن را بر روی یک مثال بررسی خواهیم کرد.

الگوی استفاده از آن به صورت زیر است:

var result = List1.DefaultIfEmpty();

به مثال زیر توجه کنید:

using System;
using System.Linq;
using System.Collections.Generic;
namespace LINQExamples

{
    class Program
    {
        static void Main(string[] args)
        {
            int[] List1 = { 1, 2, 3, 4, 5 };
            int[] List2 = { };
            var result1 = List1.DefaultIfEmpty();
            var result2 = List2.DefaultIfEmpty();
            var result3 = List2.DefaultIfEmpty(5);
            Console.WriteLine("----List1 with Values----");
            foreach (var val1 in result1)
            {
                Console.WriteLine(val1);
            }
            Console.WriteLine("---List2 without Values---");
            foreach (var val2 in result2)
            {
                Console.WriteLine(val2);
            }
            Console.WriteLine("---List2 without Values---");
            foreach (var val3 in result3)
            {
                Console.WriteLine(val3);
            }
            Console.ReadLine();
        }
    }
}

همان طور که در بالا ذکر شد اگر متد DefaultIfEmpty بر روی یک مجموعه ی دارای عضو فراخوانی شود کار Select را انجام می دهد، که صحت این موضوع را می توان با دقت به آرایه ی List1 و خروجی تایید کرد. اما به آرایه ی List2 دقت کنید که خالی از عضو است. در حالت اول متد DefaultIfEmpty بدون پارامتر ورودی فراخوانی شده، پس مقدار صفر در خروجی نمایان می شود. در حالت دوم دوباره متد DefaultIfEmpty توسط List2 فراخوانی شده اما این بار متد، یک پارامتر ورودی دلخواه دارد و کاربرد آن بدین صورت است که در صورت خالی بودن مجموعه، پارامتر وردی متد DefaultIfEmpty در خروجی چاپ می شود.

روش Query:

 var result1 = (from x in List1
                select x).DefaultIfEmpty();
 var result2 = (from x in List2
                select x).DefaultIfEmpty();
 var result3 = (from x in List2
                select x).DefaultIfEmpty(5);
خروجی مثال DefaultIfEmpty
خروجی مثال DefaultIfEmpty

این بخش از آموزش به پایان رسید. سعی کنید با استفاده از متد های بررسی شده در این بخش مثال های متعددی حل کنید چون این متدها علی رغم سادگی فرار هستند و نیاز به تمرین و تکرار دارند.

موفق باشید.

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

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.