۱۳۸۹/۰۶/۱۵

آموزش LINQ-قسمت چهارم : عملگرهای شرطی و پرتو


نکته خیلی مهم : این پست برای مطالعه توصیه نمی شود برای  یادگیری LINQ می توانید به " دوره آموزشی LINQ " مراجعه کنید.



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

public class Customer
{
 public int CustomerID;
 public string Name;
 public string Address;
 public string City;
 public string Region;
 public string PostalCode;
 public string Country;
 public string Phone;
 public List<Order> Orders;
}

public class Order
{
 public int OrderID; 
 public int CustomerID;
 public Customer Customer;
 public DateTime OrderDate;
 public decimal Total;
}

public class Product
{
 public int ProductID; 
 public string Name;
 public string Category;
 public decimal UnitPrice;
 public int UnitsInStock;
}

عملگرهای شرطی - Restriction Operators

این عملگرها عناصر یک مجموعه را بر اساس شرط داده شده به عملگر فیلتر می کنند


عملگر Where

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

public static IEnumerable<T> Where<T>(
 this IEnumerable<T> source,
 Func<T, bool> predicate);

public static IEnumerable<T> Where<T>(
 this IEnumerable<T> source,
 Func<T, int, bool> predicate);
تفاوت این دو فرم از عملگر Where در پارامتر دوم آن است این پارامتر همان شرطی است که هر عنصر در یک مجموعه با آن مقایسه می گردد.در فرم دوم عملگر Where یک پارامتر از نوع int وجود دارد که نشان دهنده اندیس هر عضو در مجموعه است و از صفر شروع می شود.
مثال زیر اجناسی که قیمت پایه آنها بیشتر از 10 است برگردانده می شود

IEnumerable<Product> x =
 from p in products
 where p.UnitPrice >= 10
 select p;
وقتی پرس وجوی بالا توسط کامپایلر،کامپایل می شود به توابع الحاقی تبدیل می شود و در واقع توابع الحاقی هستند ولی برای اینکه بتوان از همان فرم پرس وجوی SQL استفاده کنیم به این صورت نوشته می شوند و شما می توانید از هر دو فرم برای نوشتن پرس وجوهای خود استفاده کنید.

IEnumerable<Product> x = products.Where(p => p.UnitPrice >= 10);

در مثال زیر از فرم دوم عملگرWhere استفاده شده است که خریداری را که اندیس آن در لیست با کد ID آن برابر است ،برمی گرداند

List<Customer> customer = new List<Customer>
{
       New Customer { CustomerID =0,Name =”Ali”},
       New Cusyomer { CustomerID =2,name=”Ahmad”},
       New Customer { CustomerID =3,name=”Reza”}
};
Var query = customer
           .Where((p,index) => p.CustomerID == index );


توجه کنید اگر آرگومان ها Null باشند یک استثناء ArgumentNullException رخ می دهد.

عملگر OfType
این عملگر اعضاء یک مجموعه را برحسب یک عضو فیلتر می کند

private static ArrayList GetComplexArrayList ()
{ 
      System.Collections.ArrayList arrList =
             new  System.Collections.ArrayList(4);

      arrList.Add("String value One"); 
      arrList.Add("String value Two"); 
      arrList.Add("String value Three");

      arrList.Add(new Customer { CustomerID =1,
                                 Name="ali",
                                 Address="Tehran,Islamshahr" });
      arrList.Add(new Customer { CustomerID =2,
                                 Name="Hossein",
                                 Address="Tehran,Vanak"});
      arrList.Add(new Order {OrderID = 1 ,CustomerID=1});
      
      return arrList;
}
حال می خواهیم لیستی که توسط متد بالا برگردانده می شود را بر اساس مشترها فیلتر کنیم.به مثال زیر توجه کنید
ArrayList arrList = GetComplexArrayList();

// Apply OfType() to the ArrayList.
IEnumerable<Customer>  query1 = arrList.OfType<Customer>();
بدیهی است که با اجرای کد بالا در شئ query1 فقط دو شی customer در لیست وجود خواهد داشت.

عملگرهای پرتو – Projection Oprators
از این عملگرها برای تغییر شکل دادن اعضاء مجموعه ای و انتقال آن (آنها)به مجموعه دیگر استفاده می شود البته می توان اعضاء مجموعه اول را بدون تغییر در مجموعه دوم قرار داد.در ادامه این عملگرها را بررسی می کنیم.

عملگر Select
همانطور که در ابتدای این قسمت به عقب افتادن پرس و جو اشاره کردم، نتیجه  پرسو جو به عقب می افتد که عملگر Select به این صورت پیاده سازی شده.وقتی یک Select اجرا می شود که بخواهیم نتیجه پرس و جو را با یک foreach  مرور کنیم یا متد GetEnumeratorرا صدا بزنیم ،همانطور که در قسمت قبلی به این نکته اشاره کردم وقتی یک حلقه foreach  می خواهد یک شئ را پیمایش کند تابع GetEnumerator را فراخوانی می کند تا یک عضو از مجموعه را به دست آورد به همین دلیل است که در حلقه foreach اعضاء فقط خواندنی هستند.
این عملگر نیز همانند Where به دو فرم است:

public static IEnumerable<S> Select<T, S>(
 this IEnumerable<T> source,
 Func<T, S> selector);

public static IEnumerable<S> Select<T, S>(
 this IEnumerable<T> source,
 Func<T, int, S> selector);

پارامتر اول برای اول برای پردازش است و پارامتر دوم نشان دهنده اندیس در منابع آرکومان اول است(شروع از صفر).نتیجه عملگر Select هم می تواند یک شئ باشد هم یک مجموعه و همانطور که در شروع این قسمت به آن اشاره کردم در صورت شی بودم مثلا جمع و یا میانگین پرس و جو به صورت آنی انجام می شود.(این عملگر دقیقا همانند SELECT در SQL است)
در مثال زیر نام تمامی مشتریان برگردانده می شود(معادل * در SQL)

List<Customer> customer = new List<Customer>
{
   New Customer { CustomerID =0,Name =”Ali”},
   New Cusyomer { CustomerID =2,name=”Ahmad”},
   New Customer { CustomerID =3,name=”Reza”}
};
IEnumerable<string> customersNames = from itm in customer
                                           select itm.Name;
عبارت پرس و جوی بالا معادل عبارت زیر است

IEnumerable<string> cusyomerNames = customer.Select( itm=> itm.Name); 
در کد زیر از فرم دوم عملگر Select استفاده شده است  که اندیس در نوع بی نام با نام خاصیت Position  حفظ می شود

List<Customer> customer = new List<Customer>
{
   New Customer { CustomerID =0,Name ="Ali"},
   New Cusyomer { CustomerID =2,name="Ahmad"},
   New Customer { CustomerID =3,name="Reza"}
};
Var query = customer.Select( (p,index)=>
                              new{Position=index,p.CustomerID,p.Name});

در مثال زیر نام و دسته اجناسی که قیمتشان از 1000 بیشتر است به عنوان خروجی برگردانده می شود(با فرض اینکه item مجموعه ای از اجناس را در خود دارد)

var querydProduct = items.Where(itm => itm. UnitPrice > 1000)
                    .Select(itm =>
                            new { itm.Name, itm. Category })
                            .ToList();
foreach (var procItem in querydProduct)
{
     Console.WriteLine("The price of {0} is {1}", procItem.
                                   Name, procItem.UnitPrice);
}
توجه: اگر مجموعه ای که عملگر Select بر روی آن عمل می کند یا پارامترها Null باشند ،استثناء ArgumentNullException رخ می دهد.

عملگر SelectMany
عملکرد این عملگر همانند Select است ولی امکانی را عرضه تا بتوان چند عملیات را به متصل کرد که می توان آن را روی مجموعه های متفاوتی انجام داد و با حتی از نتیجه پرس و جویی قبلی استفاده کرد. 
فرم کلی این عملگر به صورت زیر است:

public static IEnumerable<S> SelectMany<T, S>(
 this IEnumerable<T> source,
 Func<T, IEnumerable<S>> selector);

public static IEnumerable<S> SelectMany<T, S>(
 this IEnumerable<T> source,
 Func<T, int, IEnumerable<S>> selector);
مثال زیر برای برگرداندن orderId و نام سفارش های که توسط مشتریان ایرانی در سال 1389 صورت گرفته است

var namesAndOrderIDs =
 customers.
 Where(c => c.Country == "Iran").
 SelectMany(c =>
  c.Orders.
  Where(o => o.OrderDate.Year == 1389).
  Select(o => new { c.Name, o.OrderID })
 );
عبارت زیر نمونه کد بالاست البته بدون استفاده مستقیم از توابع الحاقی

var namesAndOrderIDs =
 from c in customers
 where c.Country == "Iran"
 from o in c.Orders
 where o.OrderDate.Year == 2010
 select new { c.Name, o.OrderID };

توصیه : به شما توصیه می کنم که نحوه استفاده از عملگرها بوسیله توابع الحاقی (فرم اصلی) را یاد بگیرید چون کد نویسی را راحت می کند و به نظر بنده یادگیری و به حافظه سپردنشان آسان تر است.
Vote on iDevCenter

۲ نظر:

  1. تشکر ویژه بابت مطالبی که مینویسید مخصوصا linq . اگر مثالی در مورد برنامه نویسی چند لایه هم بنویسید خیلی عالی میشه .

    پاسخحذف
  2. سلام خواهش می کنم
    فعلا سرم خیلی شلوغه ولی بعد از پایان مباحث LINQ و Ribbon به مباحثی مانند برنامه نویسی لایه ای و لایه ای باLINQ و مباحث پیشرفته WPF وشاید کمی هم در مورد Asynchronous Event pattern خواهم پرداخت .

    موفق باشید،
    اقدم.

    پاسخحذف