اجرای با تاخیر (deferred execution)

قبل از پرداختن به درس های بعدی به این نکته توجه کنید که LINQ برای انجام پرس و جوها از deferred execution یا اجرای با تاخیر استفاده می کند. به زبان ساده به این معنی است که عبارت پرس و جوی واقعی اجرا نمی شود مگر زمانی که از نتایج پرس و جو استفاده شود. مثال زیر را در نظر بگیرید :

  1: int[] numbers = { 1, 2, 3, 4, 5 };
  2: 
  3: int i = 3;
  4: 
  5: var result = from n in numbers
  6:                 where n <= i  
  7:                 select n;     
  8: 
  9: i = 4;
 10: 
 11: foreach (var n in result)
 12: {
 13:     Console.WriteLine(n);
 14: }
1
2
3
4

عملگر where را در درس های بعدی به طور مفصل توضیح می دهیم ولی برای این مثال باید توضیح مختصری از آن را ارائه دهیم. عملگر where بر اساس یک شرط نتایج حاصل از یک پرس و جو را فیلتر می کند. در خط 6 از مثال بالا عملگر where فقط مقادیری را بر می گرداند که کوچکتر یا مساوی با مقدار i یعنی 3 باشند. در درس های آینده به طور مفصل در رابطه با این عملگر توضیح خواهیم داد . اجازه دهید بر روی deferred execution تمرکز کنیم.

در خط 3 مثال بالا مقدار 3 به متغیر i نسبت داده شده است.بنابراین ،وقتی عبارت پرس و جوی خطوط 7-5 اجرا می شود ، عملگر where هر عضو منبع داده را با مقدار 3 مقایسه می کند، اگر عضو مورد نظر کوچکتر یا مساوی با این مقدار باشد در نتیجه پرس و جو قرار می گیرد.

حالا ممکن است که فکر کنید که متغیر result شامل اعداد 1،2 و 3 است. اما در حقیقت اینطور نیست. از آنجاییکه در خط 9 مقدار متغیر i به 4 تغییر کرده است و به دلیل اینکه Linq از اجرای با تاخیر در بسیاری از عملگرهای خود استفاده می کند ، مقدار جدید متغیر به طور مستقیم بر روی نتیجه عبارت پرس و جو در خطوط 5 تا 7 تاثیر می گذارد. همانطور که قبلا گفته شد، این عبارت پرس وجو فقط یکبار در برنامه اجرا می شود. حلقه foreach در خطوط 11 تا 14 عبارت پرس و جو را اجرا می کند و اولین آیتم آنرا بر می گرداند. به این نکته توجه کنید چون ما مقدار متغیر i را تغییر دادیم ، عبارت پرس و جو به روز رسانی می شود. حلقه foreach به جای نمایش مقادیر 1 تا 3 مقادیر 1 تا 4 را نمایش می دهد(به این خاطر که آخرین مقدار i برابر 4 است). یکی از کاربردهای اجرای عقب افتاده نیز همین است. برنامه به شما این اجازه را می دهد که قبل از اجرای عبارت پرس و جو و بدست آوردن مقادیر آنرا به روزرسانی کنید. اجرای با تاخیر رفتار پیشفرض Linq در اجرای عبارات پرس و جو است ، اما شما می توانید آن را تغییر داده و کاری کنید که پرس و جو فورا انجام شود.

برای اینکار شما باید از مجموعه ای از متد ها در فضای نامی System.Linq استفاده کنید که شامل ToArray<T> ، ToList<T> ، ToDictionary<T> ، ToLookUp<T> می شود.

این متدها جزء متدهای الحاقی رابط IEnumerable<T> هستند. به عنوان مثال ، برای تبدیل نتیجه یک عبارت پرس و جو به کلکسیونی از نوع List<T> از متد ToList<T> استفاده می شود. مثال زیر نشان می دهد که چگونه با استفاده از متد ToList<T> بلادرنگ یک عبارت پرس و جو را اجرا کنیم:

  1: int[] numbers = { 1, 2, 3, 4, 5 };
  2:  
  3: int i = 3;
  4:  
  5: var result = (from n in numbers
  6:               where n <= i
  7:               select n).ToList();
  8:  
  9: i = 4;
 10:  
 11: foreach (var n in result)
 12: {
 13:     Console.WriteLine(n);
 14: }
1
2
3

از آنجاییکه عبارت پرس و جو در کد بالا فورا اجرا می شود، وقتی که برنامه به خط 7 می رسد، نتایج حاصل از پرس و جو در متغیر result قرار می گیرند و حتی اگر شما در خط 9 مقدار متغیر i را تغییر دهید باز هم اعداد 1 تا 3 به جای اعداد 1 تا 4 در خروجی نمایش داده می شوند.

به این نکته توجه کنید که برخی از متدهای توسعه یافته فضای نامی System.Linq باعث اجرای با تاخیر و برخی دیگر از آنها باعث اجرای فوری پرس و جو می شوند. مثلا متد ()Select برای اجرای با تاخیر و متد ()ToList برای اجرای فوری عبارت پرس و جو به کار می رود. مثلا در پرس و جوی زیر از روش متدی و متد ()Select برای به دست آوردن عناصر یک آرایه استفاده شده است:

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

از آنجاییکه متد ()Select از اجرای با تاخیر استفاده می کند، کد فوق تا زمانی که حلقه foreach تمامی نتایج را پیمایش نکند اجرا نمی شود. در عوض اگر بعد از متد ()Select از متدی که باعث اجرای فوری پرس و جو می شود مانند متد ()ToList استفاده کنید، عبارت پرس و جو فورا اجرا می شود:

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

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

متد اجرا متد اجرا
Distinct با تاخیر Reverse با تاخیر
DefaultIfEmpty با تاخیر Select با تاخیر
ElementAt فوری SelectMany با تاخیر
ElementAtOrDefault با تاخیر SequenceEqual فوری
Except با تاخیر Single فوری
First فوری SingleOrDefault فوری
FirstOrDefault فوری Skip با تاخیر
GroupBy با تاخیر SkipWhile با تاخیر
GroupJoin با تاخیر Sum فوری
Intersect با تاخیر Take با تاخیر
Join با تاخیر TakeWhile با تاخیر
Last فوری ThenBy با تاخیر
LastOrDefault فوری ThenByDescending با تاخیر
LongCount فوری ToArray فوری
Max فوری ToDictionary فوری
Min فوری ToList فوری
OfType با تاخیر ToLookup فوری
OrderBy با تاخیر Union با تاخیر
OrderByDescending با تاخیر Where با تاخیر

اگر به خاطر سپردن جدول بالا سخت است، می توانید فقط متدهایی را به خاطر بسپارید که استفاده از آنها باعث اجرای با تاخیر می شود. اگر نتیجه یک عبارت پرس و جو یک مقدار ساده مانند یک عدد و یا یک شئ باشد پس این متد از آن دسته ای است که باعث اجرای فوری پرس و جو می شوند. اما اگر نتیجه برگشتی از متد از نوع IEnumerable<T> باشد پس بدانید که بیشتر مواقع این متد از آن نوع دسته متدهایی است که از اجرای با تاخیر استفاده می کند. یک روش برای تشخیص مقدار برگشتی IEnumerable<T> این است که با نشانگر ماوس بر روی کلمه کلیدی var مکث کنید. که در این صورت با باز شدن یک پنجره popup نوع IEnumerable<T> به شما نمایش داده می شود. اگر این نوع به شما نمایش داده شد بدانید که عبارت پرس و جو از اجرای با تاخیر استفاده می کند. مثلا همانطور که در شکل زیر مشاهده می کنید از آنجاییکه متد ()Reverse از اجرای با تاخیر استفاده می کند، با مکث بر روی کلمه var پنجره popup نوع IEnumerable<T> را نشان می دهد :
deferred-execution-in-linq-01

لطفا اگر نظر، پیشنهاد و یا انتقادی در باره مطلب بالا دارید در قسمت زیر و اگر سوالی دارید در بخش پرسش و پاسخ مطرح بفرمایید.

  1. عاطفه پاسخ دادن

    سلام، ممنون از سایت خیلی خوبتون

    • یونس ابراهیمی پاسخ دادن

      سلام، لطف دارین