با سلام خدمت همراهان سایت میزفا ، با یکی دیگر از جلسات آموزش ASP.NET Core در خدمت شما دوستان هستم در جلسه گذشته با متدهای افزایشی یا Extention Methods آشنا شدید در این جلسه قصد دارم درباره عبارت های لامبدا و کاربرد آن در ASP.NET Core صحبت کنم ، این جلسه آموزشی بسیار مهم را از دست ندهید.
سرفصلهای پست
عبارت های لامبدا (Lambda)
برای درک کارایی عبارت های لامبدا ، متد گسترشی FilterByPrice که در جلسه گذشته کدنویسی کردیم را در نظر بگیرید اگر بخواهیم این متد ، ورودی ها را بر پایه نام آنها هم فیلتر کند باید متد دیگری به نام FilterByName به کلاس MyExtentionMethods.cs اضافه نمایید.این متد در کد زیر به صورت پررنگ نمایش داده شده است.
namespace LearnCSharp.Models { public static class MyExtentionMethods { public static decimal TotalPrices(this IEnumerable<Product> products) { decimal total = 0; foreach (Product prod in products) { total += prod?.Price ?? 0; } return total; } public static IEnumerable<Product> FilterByPrice(this IEnumerable<Product> productEnum, decimal minimumPrice) { foreach (Product prod in productEnum) { if ((prod?.Price ?? 0) >= minimumPrice) { yield return prod; } } } public static IEnumerable<Product> FilterByName(this IEnumerable<Product> productEnum, char firstLetter) { foreach (Product prod in productEnum) { if (prod?.Name?[0] == firstLetter) { yield return prod; } } } } }
کد زیر کاربرد هر دور متد را در کنترلر نشان میدهد .
using LearnCSharp.Models; using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace LearnCSharp.Controllers { public class HomeController : Controller { public ViewResult Index() { Product[] productArray = { new Product {Name="Kayak" ,Price=275M}, new Product {Name="Lifejacket",Price=48.95M}, new Product {Name="Soccer bail" ,Price=19.50M}, new Product {Name="Corner flag" ,Price=34.95M}, }; decimal priceFilterTotal = productArray.FilterByPrice(20).TotalPrices(); decimal nameFilterTotal = productArray.FilterByName('S').TotalPrices(); return View("Index", new string[] { $"Price Total:{priceFilterTotal:C2}",$"Name Total:{nameFilterTotal:C2}" }); } } }
فیلتر نخست کالاهایی را جدا میکند که قیمت آنها بیش از ۱۰ دلار باشد در حالی که فیلتر دوم کالاهایی که نام آنها با حرف S شروع شوند را فیلتر میکند اجرای برنامه خروجی زیر را نشان خواهد داد.
Price Total: $358.90 Name Total: $19.50
تعریف تابع با عبارت لامبدا
شما می توانید برای فیلتر کردن داده ها بر پایه ی هر خاصیت کلاس و یا هر ترکیبی از خاصیت های آن ، هربار متد جدیدی به کلاس MyExtensionMethods اضافه کنید . #C روش بهتری با امکان ارسال توابع به متد ، به عنوان پارامتر متد پیشنهاد میکند . کد زیر متدی گسترشی را نشان میدهد که کلکسیونی از نمونه های Product را فیلتر میکند ولی انتخاب اینکه چه نتیجه ای در خروجی به دست آید را به تابعی دیگر میسپارد.
namespace LearnCSharp.Models { public static class MyExtentionMethods { public static decimal TotalPrices(this IEnumerable<Product> products) { decimal total = 0; foreach (Product prod in products) { total += prod?.Price ?? 0; } return total; } public static IEnumerable<Product> Filter(this IEnumerable<Product> productEnum, Func<Product, bool> selector) { foreach (Product prod in productEnum) { if (selector(prod)) { yield return prod; } } } } }
پارامتر دوم متد ، تابعی است که شیئی از Product را پذیرفته و نتیجه ای منطقی را باز میگرداند این تابع برای هر یک از نمونه های Product فراخوانی میشود و چنانچه مقدار بازگشتی تابع true باشد ، نمونه ی یاد شده در خروجی بازگشت داده میشود . برای استفاده از این فیلتر ، میتوان متدی را مشخص کرد و یا تابع جدیدی ایجاد کرد.
using System; using LearnCSharp.Models; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; namespace LearnCSharp.Controllers { public class HomeController : Controller { bool FilterByPrice(Product p) { return (p?.Price ?? 0) >= 20; } public ViewResult Index() { Product[] productArray = { new Product {Name="Kayak" ,Price=275M}, new Product {Name="Lifejacket",Price=48.95M}, new Product {Name="Soccer bail" ,Price=19.50M}, new Product {Name="Corner flag" ,Price=34.95M}, }; Func<Product, bool> nameFilter = delegate (Product prod) { return prod?.Name?[0] == 'S'; }; decimal priceFilterTotal = productArray.Filter(FilterByPrice).TotalPrices(); decimal nameFilterTotal = productArray.Filter(nameFilter).TotalPrices(); return View("Index",new string[] {$"Price Total:{priceFilterTotal:C2}",$"Name Total:{nameFilterTotal:C2}"}); } } }
هیچ کدام از این روش ها از نظر برنامهنویسی بهینه و کارامد نیستند ایجاد تابعی به شکل <Func <Product,bool روان و خوش فرم نیست همانگونه که کد زیر نشان میدهد عبارت های لامبدا امکان تعریف توابع مورد نیاز را به صورت موثرتر وخواناتری فراهم میآورند .
using System; using LearnCSharp.Models; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; namespace LearnCSharp.Controllers { public class HomeController : Controller { bool FilterByPrice(Product p) { return (p?.Price ?? 0) >= 20; } public ViewResult Index() { Product[] productArray = { new Product {Name="Kayak" ,Price=275M}, new Product {Name="Lifejacket",Price=48.95M}, new Product {Name="Soccer bail" ,Price=19.50M}, new Product {Name="Corner flag" ,Price=34.95M}, }; Func<Product, bool> nameFilter = delegate (Product prod) { return prod?.Name?[0] == 'S'; }; decimal priceFilterTotal = productArray.Filter(p=>(p?.Price ?? 0) >=20).TotalPrices(); decimal nameFilterTotal = productArray.Filter(p=>p?.Name?[0]=='S').TotalPrices(); return View("Index",new string[] {$"Price Total:{priceFilterTotal:C2}",$"Name Total:{nameFilterTotal:C2}"}); } } }
در کد بالا پارامترها بدون مشخص شدن نوع آورده شده اند نماد <= را می توانید به شکل “نتیجه می دهد” بخوانید این نماد پارامترها را به نتیجه ی برگشتی پیوند می دهد در مثال ما ، پارامتری از نوع Product به نام P ، باید نتیجه ی منطقی به دست دهد این مقدار در عبارت نخست، چنانچه قیمت بیشتر از ۲۰ باشد true و در عبارت دوم زمانی true است که خاصیت Name با حرف S شروع شوند (P به این دلیل شیئی از Product است که عبارت لامبدا بر روی آرایه ی productArray که دارای عناصر Product است اجرا میشود .)
لزومی ندارد که منطق عملیات مورد نظر در عبارت لامبدا آورده شود روش دیگر این است که متدی جداگانه بنویسید ودر عبارت لامبدا فراخوانی کنید به صورت زیر :
prod=>EvaluateProduct(prod)
اگر تعداد پارامترهای ورودی بیش از یکی باشد باید از نماد پرانتز استفاده کرد همانند کد زیر :
(prod,count)=> prod.Price > 20 && count > 0
و در پایان اگر بخواهیم منطق کد را در عبارت لامبدا بیاوریم و تعداد خط های کد بیش از یکی باشد از نمادهای {} برای ایجاد بلاکی از کد استفاده میشود .
(prod,count)=> { //.... multiple code statements... return result; }
عبارت های لامبدا برای متدها و خصوصیت ها
در نگارش 6.0 #C کارایی عبارت های لامبدا گسترش یافته است به طوری که آنها را میتوانید برای ایجاد متد و یا خصوصیات نیز به کار برید. در MVC به ویژه در کنترلرها ، با متدهایی برخورد میکنید که باید با یک خط کد ، داده هایی را برای نمایش به نما بفرستید در کد زیر اکشن Index با پیروی از این تفکر نوشته شده است.
using System; using LearnCSharp.Models; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using System.Linq; namespace LearnCSharp.Controllers { public class HomeController : Controller { public ViewResult Index() { return View(Product.GetProducts().Select(p=>p?.Name)); } } }
متد اکشن کلکسیونی از نمونه های Product را از متد ()Product.GetProducts گرفته واز LINQ برای ارسال مقدار Name آنها برای نما استفاده میکند اجرای برنامه خروجی زیر را نشان میدهد.
Kayak Lifejacket
از آنجایی که متد () GetProducts در برگشتی های خود ، عنصری تهی را هم نتیجه میدهد ، یک عنصر خالی هم در پنجره مرورگر خواهید داشت.
اگر بدنهی متدی بیشتر از یک عبارت نداشته باشد میتوان برای آن ، از عبارت لامبدا به صورت زیر استفاده کرد.
using System; using LearnCSharp.Models; using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using System.Linq; namespace LearnCSharp.Controllers { public class HomeController : Controller { public ViewResult Index()=> View(Product.GetProducts().Select(p=>p?.Name)); } }
زمانی که عبارت لامبدا برای ایجاد متد به کار میرود ، فرمان return حذف میشود و برای ارتباط دادن الگوی متد به پیاده سازی آن از نماد <= استفاده میشود.
همین روش را میتوانید برای ایجاد خاصیت ها به کار برید. کد زیر نمونه ای از این کار را در کلاس Product نشان میدهد.
namespace LearnCSharp.Models { public class Product { public Product(bool stock = true) { InStock = stock; } public string Name { get; set; } public string Category { get; set; } = "Watesports"; public decimal? Price { get; set; } public bool InStock { get; } = true; public Product Related { get; set; } public bool NameBrginsWithS => Name?[0] == 'S'; public static Product[] GetProducts() { Product Kayak = new Product { Name = "Kayak", Category = "Water Craft", Price = 275M }; Product lifejacket = new Product { Name = "Lifejacket", Price = 48.95M, }; Kayak.Related = lifejacket; return new Product[] { Kayak, lifejacket, null }; } } }
به پایان این جلسه آموزشی بسیار مهم رسیدیم در صورتی که سوالی پیرامون مقالات آموزشی داشتید حتما در قسمت نظرات مطرح کنید.
2 پاسخ
کاش یه ویدیو رکورد میکردین
با سلام .. به زودی ادامه دوره آموزشی به صورت ویدیویی در سایت قرار خواهد گرفت.