مطالب
معرفی Bit Platform
پلتفرم Bit یک پروژه تماما Open Source در GitHub میباشد که هدف آن تسهیل توسعه نرم افزار با کیفیت و پرفرمنس بالا بر بستر ASP.NET Core و زبان #C است که با آن بتوان فقط با یکبار کدنویسی و با کمک استانداردهای وب همچون HTML / CSS و Web Assembly ، خروجیهایی چون Android / iOS / Windows را با دسترسی کامل به امکانات سیستمعامل به همراه برنامههای تحت وب SPA و PWA (با یا بدون Pre-Rendering) گرفت.
پلتفرم Bit تا به اینجا از دو قسمت Bit Blazor Components (شامل بیش از ۳۰ کمپوننت کاربردی، کم حجم و High Performance مانند Tree / Multi Select / Data Grid / Date Picker / Color Picker و...) به همراه Bit Project Templates (قالب پروژههای حاوی امکانات پر استفاده) تشکیل شده است.
برخی مواردی که در رابطه با آنها صحبت شد، ممکن است برای شما آشنا نباشند، بنابراین قبل از بررسی مفصلتر Bit Platform، نگاهی به آن میاندازیم:
وب اسمبلی چیست؟
برای درک بهتر وب اسمبلی ابتدا باید بدانیم این تکنولوژی اصلا از کجا آمده و هدف آن چیست؟
میدانیم که مرورگرها پروایدر صفحات وب هستند و ما برای اینکه بتوانیم اپلیکیشنی که ساختیم را در محیط وب برای کاربران به اشتراک بگذاریم باید از این مرورگرها و زبان ارتباطی آنها پیروی کنیم. این زبانهای ارتباطی مشخصا سه چیز است: HTML CSS JavaScript
اما آیا راهی هست که بتوان بجای JavaScript از زبانهای دیگر هم در سمت مرورگر استفاده کرد؟
وب اسمبلی یا همان WASM، آمده تا به ما اجازه دهد از هر زبانی که خروجی به Web Assembly دارد، برای تعاملات UI استفاده کنیم. یعنی با زبان هایی مثل #C / C++ / C و... میتوان کدی نوشت که مرورگر آن را اجرا کند. این یک تحول بزرگ است که امروزه تمامی مرورگرها (به جز مرورگرهایی که از دور خارج شده اند) از آن پشتیانی میکنند چرا که Web Assembly به یکی از اجزای استاندارد وب تبدیل شده است.
اطلاعات بیشتر در رابطه با وب اسمبلی را میتوانید از این مقاله بخوانید.
تعریف SPA و PWA:
SPA: Single Page Application
PWA: Progressive Web Application
در گذشته برای رندر کردن صفحات وب با عوض شدن URL یا درخواست کاربر برای دریافت اطلاعات جدید از سرور و نمایش آن ، صفحه مرورگر ملزم به رفرش شدن مجدد و از سر گیری کل فرایند تولید HTML میشد. طبیعتا این تکرار برای کاربر هنگام استفاده از اپلیکیشن خیلی خوشایند نبود چرا که هربار میبایست زمانی بیشتر صرف تولید مجدد صفحات را منتظر میماند. اما در مقابل آن Single Page Application (SPA)ها این پروسه را تغیر داد.
در رویکرد SPA، کل CSS , HTML و JS ای که برای اجرای هر صفحه ای از اپلیکیشن نیاز هست در همان لود اولیه برنامه توسط مرورگر دانلود خواهد شد. با این روش هنگام تغییر URL صفحات مرورگر دیگر نیازی به لود دوباره اسکریپتها ندارد. همچنین وقتی قرار است اطلاعات جدیدی از سرور آپدیت و نمایش داده شود این درخواست بصورت یک دستور Ajax به سرور ارسال شده و سرور با فرمت JSON اطلاعات درخواست شده را پاسخ میدهد. در نهایت مرورگر نیز اطلاعات برگشتی از سرور را مجدد جای گذاری میکند و کل این روند بدون هیچگونه رفرش دوباره صفحه انجام میشود.
در نتیجهی این امر، کاربر تجربه خوشایندتری هنگام کار کردن با SPAها خواهد داشت. اما همانقدر که این تجربه در طول زمان استفاده از برنامه بهبود یافت، لود اولیه اپلیکیشن را کندتر کرد، چرا که اپلیکیشن میبایست همه کدهای مورد نیاز خود برای صفحاتش را در همان ابتدا دانلود کند.(در ادامه با قابلیت Pre-Rendering این اشکال را تا حدود زیادی رفع میکنیم)
با استفاده از PWA میتوانید وبسایتهای SPA را بصورت یک برنامه نصبی و تمام صفحه، با آیکن مجزایی همانند اپلیکیشنهای دیگر در موبایل و دسکتاپ داشته باشید. همچنین وقتی از PWA استفاده میکنیم برنامه وب میتواند به صورت آفلاین نیز کار کند.
البته حتی در برنامههایی که لازم نیست آفلاین کار کنند، در صورت قطعی ارتباط کاربر با شبکه، به جای دیدن دایناسور معروف، اینکه برنامه در هر حالتی باز شود و به صورتی کاربر پسند و قطعی نت به وی اعلام شود ایده خیلی بهتری است (":
قابلیت Pre-Rendering:
هدف Pre-Rendering بهبود گشت گذار کاربر در سایت است. شیوه کارکرد آن به این صورت است که وقتی کاربر وارد وبسایت میشود، سرور در همان ابتدای کار و جلوتر از اتمام دانلود اسمبلیها، فایلی حاوی HTML ، CSSهای صفحه ای که کاربر درخواست کرده را در سمت سرور میسازد و به مرورگر ارسال میکند. در همین حین، اسمبلیها نیز توسط مرورگر دانلود میشوند و برنامه از محتوای صرف خارج شده و حالت تعاملی میگیرد. اصطلاحا به این قابلیت Server-Side Rendering(SSR) نیز میگویند. در این حالت کاربر زودتر محتوایی از برنامه را میبیند و تجربه بهتری خواهد داشت. این امر در بررسی Search Engineها و سئو وبسایت نیز تاثیر بسزایی دارد.
نگاهی به Blazor:
تا اینجا هر آنچه که نیاز بود برای درک بهتر از Blazor بدانیم را فهمیدیم، اما خود Blazor چیست؟ در یک توضیح کوتاه، فریمورکی ارائه شده توسط مایکروسافت میباشد برای پیادهسازی UI و منطق برنامهها شامل امکانات Routing ، Binding و...
بلیزر در انواع مختلفی ارائه شده که هرکدام کاربرد مشخصی دارد:
Blazor Server
در بلیزر سرور پردازشها جهت تعامل UI درون سرور اجرا خواهد شد. برای مثال وقتی کاربر روی دکمه ای کلیک میکند و آن دکمه مقداری عددی را افزایش میدهد که از قضا متن یک Label به آن عدد وابسته است، رویداد کلیک شدن این دکمه توسط SignalR WebSocket به سرور ارسال شده و سرور تغیر متن Label را روی همان وب سوکت به کلاینت ارسال میکند.
با توجه به این که تعامل کاربر با صفحات برنامه، بسته به میزان کندی اینترنت کاربر، ممکن است کند شود و همچنین Blazor Server قابلیت PWA شدن ندارد و علاوه بر این بار پردازش زیادی روی سرور میاندازد (بسته به پیچیدگی پروژه) و در نهایت ممکن است در آن از Component هایی استفاده کنیم که چون در حالت Blazor Server پردازش سمت سرور بوده، متوجه حجم دانلود بالای آنها نشویم و کمی بعدتر که با Blazor Hybrid میخواهیم خروجی Android / iOS بگیریم متوجه حجم بالای آنها شویم، استفاده از Blazor Server را در Production توصیه نمیکنیم، ولی این حالت برای Debugging بهترین تجربه را ارائه میدهد، بالاخص با امکان Hot Reload و دیدن آنی تغییرات C# / HTML / CSS در ظاهر و رفتار برنامه موقع کدنویسی.
Blazor WebAssembly
در بلیزر وب اسمبلی منطق مثال قبلی که با C# .NET نوشته شده است، روی مرورگر و با کمک Web Assembly اجرا میشود و نیازی به ارتباط جاری با سرور توسط SignalR نیست. این باعث میشود که با بلیزر وب اسمبلی بتوان اپلیکیشنهای PWA نیز نوشت.
یک برنامه Blazor Web Assembly میتواند چیزی در حدود دو الی سه مگ حجم داشته باشد که در دنیای امروزه حجم بالایی به حساب نمیاید، با این حال با کمک Pre Rendering و CDN میتوان تجربه کاربر را تا حدود زیادی بهبود داد.
برای مثال سایت Componentهای Bit Platform جزو معدود دموهای Componentهای Blazor است که در حالت Blazor Web Assembly ارائه میشود و شما میتوانید با باز کردن آن، تجربه حدودی کاربرانتان را در حین استفاده از Blazor Web Assembly ببینید. به علاوه، در dotnet 7 سرعت عملکرد Blazor Web Assembly بهبود قابل توجهی پیدا کرده است.
Blazor Hybrid
از Blazor Hybrid زمانی استفاده میکنیم که بخواهیم برنامههای موبایل را برای Android , iOS و برنامههای Desktop را برای ویندوز، با کمک HTML , CSS توسعه دهیم. نکته اصلی در Blazor Hybrid این است که اگر چه از Web View برای نمایش HTML / CSS استفاده شده، اما منطق سمت کلاینت برنامه که با C# .NET توسعه داده شده است، بیرون Web View و به صورت Native اجرا میشود که ضمن داشتن Performance بالا، به تمامی امکانات سیستم عامل دسترسی دارد. علاوه بر دسترسی به کل امکانات Android / iOS Sdk در همان C# .NET ، عمده کتابخانههای مطرح مانند Firebase، با ابزار Binding Library ارائه شده توسط مایکروسافت، دارای Wrapper قابل استفاده در C# .NET هستند و ساختن Wrapper برای هر کتابخانه Objective-C ، Kotlin، Java، Swift با این ابزار فراهم است.
اگر شما در حال حاضر فقط #HTML , CSS , C بدانید، اکنون با بلیزر میتوانید هر اپلیکیشنی که بخواهید توسعه دهید. از وبسایتهای SPA گرفته تا اپهای موبایل Android ، IOS و برنامههای دسکتاپی برای Windows , Mac و بزودی نیز برای Linux
معرفی پکیج Bit Blazor UI:
پکیج Bit Blazor UI مجموعه ای از کامپوننتهای مرسومی است که بر پایه بلیزر نوشته شده و به ما این امکان را میدهد تا المانهای پیچیده ای مثل Date Picker , Grid , Color Picker , File Upload , DropDown و بسیاری از المانهای دیگر را با شکلی بهینه، بدون نیاز به اینکه خودمان بخواهیم برای هر یک از اینها از نو کدنویسی کنیم، آن را در اختیار داشته باشیم.
عمده مشکل کامپوننتهای ارائه شده برای بلیزر حجم نسبتا بالای آن است که باعث میشد بیشتر در مصارفی از قبیل ایجاد Admin Panel کارایی داشته باشد. اما این موضوع به خوبی در Bit Blazor UI مدیریت شده و در حال حاضر با بیش از 30 کامپوننت با حجم بسیار پایینی، چیزی حدود 200 کیلوبایت قابل نصب است. از لحاظ حجمی نسبت به رقبای خود برتری منحصر به فردی دارد که باعث میشود به راحتی حتی در اپلیکیشنهای موبایل هم قابل استفاده باشد و کماکان پرفرمنس خوبی ارائه دهد.
این کامپوننتها با ظاهر Fluent پیاده سازی شده و میتوانید لیست کامپوننتهای بلیزر Bit را از این لینک ببینید.
معرفی Bit TodoTemplate:
قبل از اینکه به معرفی Bit TodoTemplate بپردازیم باید بدانیم که اصلا پروژههای Template چه هستند. در واقع وقتی شما Visual Studio را باز میکنید و روی گزینه Create New Project کلیک میکنید با لیستی از پروژههای تمپلیت روبرو میشوید که هرکدام چهارچوب خاصی را با اهدافی متفاوت در اختیارتان قرار میدهند.
Bit Platform هم Project Template ای با نام TodoTemplate توسعه داده که میتوانید پروژههای خودتان را از روی آن بسازید، اما چه امکاناتی به ما میدهد؟
در یک جمله، هر آنچه تا به اینجا توضیح داده شد بصورت یکجا در TodoTemplate وجود دارد.
در واقع TodoTemplate چهارچوبی را فراهم کرده تا شما تنها با دانستن همین مفاهیمی که در این مقاله خواندید، از همان ابتدا امکاناتی چون صفحات SignUp، SignIn یا Email Confirmation و... را داشته باشید و در نهایت بتوانید تمامی خروجیهای قابل تصور را بگیرید.
اما چگونه؟
در TodoTemplate همه این قابلیتها تنها درون یک فایل و با کمترین تغیر ممکن نوع خروجی کدی که نوشته اید را مشخص میکند. این تنظیمات به شکل زیر است :
<BlazorMode> ... </BlazorMode> <WebAppDeploymentType> ... </WebAppDeploymentType>
شما میتوانید با تنظیم <BlazorMode> بین انواع hosting modelهای بلیزر سوییچ کنید. مثلا برای زمانی که در محیط development هستید نوع بلیزر را Blazor Server قرار دهید تا از قابلیتهای debugging بهتری برخوردار باشید ، وقتی میخواهید وبسایت تکمیل شده تان را بصورت SPA / PWA پابلیش کنید نوع بلیزر را به Blazor WebAssembly و برای پابلیشهای Android ، IOS ، Windows ، Mac نوع بلیزر را به Blazor Hybrid تغیر دهید.
به علاوه، شما تنها با تغیر <WebAppDeploymentType> قادر خواهید بود بین SPA (پیش فرض)، SSR و PWA سوئیچ کنید.
قابلیتهای TodoTemplate در اینجا به پایان نمیرسد و بخشی دیگر از این قابلیتها به شرح زیر است :
- وجود سیستم Exception handling در سرور و کلاینت (این موضوع به گونه ای بر اساس Best Practiceها پیاده سازی شده که اپلیکیشن را از بروز هر خطایی که بخواهد موجب Crash کردن برنامه شود ایزوله کرده)
- وجود سیستم User Authentication بر اساس JWT که شما در همان ابتدا که از این تمپلیت پروژه جدیدی میسازید صفحات SignIn ، SignUp را خواهید داشت.
- پکیج Bit Blazor UI که بالاتر درمورد آن صحبت کرده ایم از همان ابتدا در TodoTemplate نصب و تنظیم شده تا بتوانید به راحتی صفحات جدید با استفاده از آن بسازید.
- کانفیگ استاندارد Swagger در سمت سرور.
- ارسال ایمیل در روند SignUp.
- وجود خاصیت AutoInject برای سادهسازی تزریق وابستگی ها.
- و بسیاری موراد دیگر که در داکیومنتهای پروژه میتوانید آنهارا ببینید.
با استفاده از TodoTemplate پروژه ای با نام Todo ساخته شده که میتوانید چندین مدل از خروجیهای این پروژه را در لینکهای پایین ببینید و پرفرمنس آن را بررسی کنید.
توجه داشته باشید هدف TodoTemplate ارائه ساختار Clean Architecture نبوده ، بلکه هدف ارائه بیشترین امکانات با سادهترین حالت کدنویسی ممکن بوده که قابل استفاده برای همگان باشد و شما میتوانید از هر پترنی که میخواهید براحتی در آن استفاده کنید.
پلتفرم Bit یک تیم توسعه کاملا فعال تشکیل داده که بطور مداوم در حال بررسی و آنالیز خطاهای احتمالی ، ایشوهای ثبت شده و افزودن قابلیتهای جدید میباشد که شما به محض استفاده از این محصولات میتوانید در صورت بروز هر اشکال فنی برای آن ایشو ثبت کنید تا تیم مربوطه آن را بررسی و در دستور کار قرار دهد. در ادامه پلتفرم Bit قصد دارد بزودی تمپلیت جدیدی با نام Admin Panel Template با امکاناتی مناسب برای Admin Panel مثل Dashboard و Chart و... با تمرکز بر Clean Architecture نیز ارائه کند. چیزی که مشخص است اوپن سورس بودن تقریبا %100 کارها میباشد از جلسات و گزارشات کاری گرفته تا جزئیات کارهایی که انجام میشود و مسیری که در آینده این پروژه طی خواهد کرد.
میتوانید اطلاعات بیشتر و مرحله به مرحله برای شروع استفاده از این ابزارها را در منابعی که معرفی میشود دنبال کنید.
منابع:
مشارکت در پروژه:
- شما میتوانید این پروژه را در گیتهاب مشاهده کنید.
- برای اشکالات یا قابلیت هایی که میخواهید برطرف شود Issue ثبت کنید.
- پروژه را Fork کنید و Star دهید.
- ایشوهایی که وجود دارد را برطرف کنید و Pull Request ارسال کنید.
- برای در جریان بودن از روند توسعه در جلسات برنامه ریزی (Planning Meeting) و گزارشات هفتگی (Standup Meeting ) که همه اینها در Microsoft Teams برگزار میشود شرکت کنید.
نظرات اشتراکها
NET Framework 4.7. منتشر شد
دریافت نگارش آفلاین دات نت 4.7
NDP47-KB3186497-x86-x64-AllOS-ENU.exe (مخصوص سرورها و کلاینتها)
NDP47-DevPack-KB3186612-ENU.exe (مخصوص توسعه دهندهها جهت یکپارچگی با ویژوال استودیو)
برای نصب آن بر روی ویندوزهای 7، 2008 و 2012 نیاز به نصب وابستگی ذیل هم هست:
NDP47-KB3186497-x86-x64-AllOS-ENU.exe (مخصوص سرورها و کلاینتها)
NDP47-DevPack-KB3186612-ENU.exe (مخصوص توسعه دهندهها جهت یکپارچگی با ویژوال استودیو)
برای نصب آن بر روی ویندوزهای 7، 2008 و 2012 نیاز به نصب وابستگی ذیل هم هست:
نظرات مطالب
پایان پروژه ASP.NET Ajax Control Toolkit !
ضمنا یک مورد رو در بارهی LINQ to SQL و ASP کلاسیک اضافه کنم. جایگزین شدن entity framework بجای L2S یا ASP.NET به جای ASP کلاسیک، یک روند سالم و سلامت توسعه است. LINQ to SQL فقط محدود است به SQL Server اما الان اکثر بانکهای اطلاعاتی موجود پروایدر EF دارند و مدل توسعهی آن بسته نیست. ASP کلاسیک رو نمیدونم باهاش کار کرده بودید یا نه؟ رسما یک فاجعه بود! مخلوطی از کدهای برنامه داخل کدهای HTML و وابستگی آن به اشیاء COM و غیره (اگر میخواستید مثلا رمزنگاری را به آن اضافه کنید باید Active-X مینوشتید و در سرور رجیستر میکردید!). این مورد اصلا قابل قیاس نیست با ASP.NET و امکانات دات نت فریم ورک.
حین کار با ASP.NET Identity به اینترفیسی به نام IIdentityMessageService شبیه به اینترفیس ذیل میرسیم:
فرض کنید از آن دو پیاده سازی در برنامه برای ارسال پیامها توسط ایمیل و همچنین توسط SMS، وجود دارد:
اکنون کلاس مدیریت کاربران برنامه، در سازندهی خود نیاز به دو وهله، از این سرویسهای متفاوت، اما در اصل مشتق شدهی از یک اینترفیس دارد:
در این حالت صرف تنظیمات ابتدایی انتساب یک اینترفیس، به یک کلاس مشخص کافی نیست:
از این جهت که در سازندهی کلاس UsersManagerService دقیقا مشخص نیست، پارامتر اول باید سرویس SMS باشد یا ایمیل؟
برای حل این مشکل میتوان به نحو ذیل عمل کرد:
در اینجا توسط متد Ctor که مخفف Constructor یا سازندهی کلاس است، مشخص میکنیم که اگر به پارامتر smsService رسیدی، از کلاس SmsService استفاده کن و در مورد کلاس سرویس ایمیل نیز به همین ترتیب. اینبار اگر برنامه را اجرا کنیم:
همانطور که در تصویر مشخص است، هر کدام از پارامترها، توسط کلاسهای متفاوتی مقدار دهی شدهاند؛ هرچند از یک اینترفیس مشخص استفاده میکنند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید:
Dependency-Injection-Samples/DI09
namespace SameInterfaceDifferentClasses.Services.Contracts { public interface IMessageService { void Send(string message); } }
public class EmailService : IMessageService { public void Send(string message) { // ... } } public class SmsService : IMessageService { public void Send(string message) { //todo: ... } }
public interface IUsersManagerService { void ValidateUserByEmail(int id); } public class UsersManagerService : IUsersManagerService { private readonly IMessageService _emailService; private readonly IMessageService _smsService; public UsersManagerService(IMessageService emailService, IMessageService smsService) { _emailService = emailService; _smsService = smsService; } public void ValidateUserByEmail(int id) { _emailService.Send("Validated."); } }
ioc.For<IMessageService>().Use<SmsService>(); ioc.For<IMessageService>().Use<EmailService>();
برای حل این مشکل میتوان به نحو ذیل عمل کرد:
public static class SmObjectFactory { private static readonly Lazy<Container> _containerBuilder = new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication); public static IContainer Container { get { return _containerBuilder.Value; } } private static Container defaultContainer() { return new Container(ioc => { // map same interface to different concrete classes ioc.For<IMessageService>().Use<SmsService>(); ioc.For<IMessageService>().Use<EmailService>(); ioc.For<IUsersManagerService>().Use<UsersManagerService>() .Ctor<IMessageService>("smsService").Is<SmsService>() .Ctor<IMessageService>("emailService").Is<EmailService>(); }); } }
var usersManagerService = SmObjectFactory.Container.GetInstance<IUsersManagerService>(); usersManagerService.ValidateUserByEmail(id: 1);
همانطور که در تصویر مشخص است، هر کدام از پارامترها، توسط کلاسهای متفاوتی مقدار دهی شدهاند؛ هرچند از یک اینترفیس مشخص استفاده میکنند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید:
Dependency-Injection-Samples/DI09
Default Compile Item Values in the the .NET Core SDK : لیستی که در این ماخذ ذکر شده به صورت خودکار به پروژه اضافه میشوند. اگر این لیست را مجددا به فایل csproj اضافه کنید، دوبار به پروژه الحاق خواهند شد که سبب بروز خطای فوق میگردد. برای مثال نیازی به ذکر یک چنین تنظیمی دیگر نیست و ذکر مجدد آن خطای محتوای تکراری را ایجاد میکند:
<ItemGroup> <Content Include="wwwroot\**" /> </ItemGroup>
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- ریستارت کردن سایت ASP.NET
- انتشار نسخه نهایی راهنمای امنیتی WCF
- صنعت حل کپچای هند و راهحلهای مقابله با اسپم با تکیه بر تحلیل محتوا
- بالابردن ضریب موفقیت پیاده سازی ITIL در سازمان
- تکست باکس قابل ویرایش بوسیله ASP.NET AJAX
- لیستی از نرم افزارهای ORMapper
- استفاده مشترک از یک یا چند User Control در چندین IIS Application
Visual Studio
ASP. Net
طراحی و توسعه وب
- تعیین اعتبار زیباتر فیلدهای یک فرم
- آشنایی با یک سری تگ مجهور HTML !
- در انتخاب رنگهای مناسب و هماهنگ مشکل دارید؟
- معرفی 10 فریم ورک جاوا اسکریپتی
اسکیوال سرور
عمومی دات نت
ویندوز
متفرقه
اشتراکها
نگاهی به بهبودهای کارآیی در NET 7.
همه ما با ابزار تزریق وابستگی مایکروسافت کار کردهایم و میدانیم برای معرفی وابستگیها میبایست آنها را به IServiceCollection معرفی کنیم. اما راه حل سادهتری هم وجود دارد. چیزی که در ابزارهای java-spring به صورت خودکار و با استفاده از annotationهایی که در component models تعریف میشوند اینکار را تسهیل میکند و دیگر خبری از یک فایل شلوغ برای معرفی وابستگیها به موتور تزریق وابستگی نیست. برای اینکار در c-sharp با استفاده از موتور تزریق وابستگی مایکروسافت، Reguto را آماده کردم و بسیار ساده با استفاده از attributeهایی که به صورت پیشفرض تعریف شده است میتوانید وابستگیهای خود را معرفی کنید.
delegateها، نوعهایی هستند که ارجاعی را به یک متد دارند؛ بسیار شبیه به function pointers در C و CPP هستند، اما برخلاف آنها، delegates شیءگرا بوده، به امضای متد اهمیت داده و همچنین کد مدیریت شده و امن به شمار میروند.
سیر تکاملی delegates را در مثال ساده زیر میتوان ملاحظه کرد:
معنای کلمه delegate، واگذاری مسئولیت است. به این معنا که ما در متد UseDelegate، نمیدانیم addMethod به چه نحوی تعریف خواهد شد. فقط میدانیم که امضای آن چیست.
در دات نت یک، یک وهله از شیء AddMethodDelegate ساخته شده و سپس متدی که امضایی متناسب و متناظر با آن را داشت، به عنوان متد انجام دهنده مسئولیت معرفی میشد. در دات نت دو، اندکی نحوه تعریف delegates با ارائه delegates بینام، سادهتر شد و در دات نت سه و نیم با ارائه lambda expressions ، تعریف و استفاده از delegates باز هم سادهتر و زیباتر گردید.
به علاوه در دات نت 3 و نیم، دو Generic delegate به نامهای Action و Func نیز ارائه گردیدهاند که به طور کامل جایگزین تعریف طولانی delegates در کدهای پس از دات نت سه و نیم شدهاند. تفاوتهای این دو نیز بسیار ساده است:
اگر قرار است واگذاری قسمتی از کد را به متدی محول کنید که مقداری را بازگشت میدهد، از Func و اگر این متد خروجی ندارد از Action استفاده نمائید:
در دو مثال فوق، نحوه تعریف inline یک Action و یا Func را ملاحظه میکنید. Action به متدی اشاره میکند که خروجی ندارد و در اینجا تنها یک ورودی int را میپذیرد. Func در اینجا به تابعی اشاره میکند که یک ورودی int را دریافت کرده و یک خروجی string را باز میگرداند.
پس از این مقدمه، در ادامه قصد داریم مثالهای دنیای واقعی Action و Func را که در سالهای اخیر بسیار متداول شدهاند، بررسی کنیم.
مثال یک) ساده سازی تعاریف API ارائه شده به استفاده کنندگان از کتابخانههای ما
عنوان شد که کار delegates، واگذاری مسئولیت انجام کاری به کلاسهای دیگر است. این مورد شما را به یاد کاربردهای interfaceها نمیاندازد؟
در interfaceها نیز یک قرارداد کلی تعریف شده و سپس کدهای یک کتابخانه، تنها با امضای متدها و خواص تعریف شده در آن کار میکنند و کتابخانه ما نمیداند که این متدها قرار است چه پیاده سازی خاصی را داشته باشند.
برای نمونه طراحی API زیر را درنظر بگیرید که در آن یک interface جدید تعریف شده که تنها حاوی یک متد است. سپس کلاس Runner از این interface استفاده میکند:
در اینجا ابتدا باید این interface را در طی یک کلاس جدید (مثلا HelloSchedule) پیاده سازی کرد و سپس حاصل را در کلاس Runner استفاده نمود.
نظر شما در مورد این طراحی ساده شده چیست؟
با توجه به اینکه هدف از معرفی interface در طراحی اول، واگذاری مسئولیت نحوه تعریف متد Run به کلاسی دیگر است، به همین طراحی با استفاده از یک Action delegate نیز میتوان رسید. مهمترین مزیت آن، حجم بسیار کمتر کدنویسی استفاده کننده نهایی از API تعریف شده ما است. به علاوه امکان inline coding نیز فراهم گردیده است و در همان محل تعریف Action، بدنه آنرا نیز میتوان تعریف کرد.
بدیهی است delegates نمیتوانند به طور کامل جای interfaceها را پر کنند. اگر نیاز است قرارداد تهیه شده بین ما و استفاده کنندگان از کتابخانه، حاوی بیش از یک متد باشد، استفاده از interfaceها بهتر هستند.
از دیدگاه بسیاری از طراحان API، اشیاء delegate معادل interface ایی با یک متد هستند و یک وهله از delegate معادل وهلهای از کلاسی است که یک interface را پیاده سازی کردهاست.
علت استفاده بیش از حد interfaceها در سایر زبانها برای ابتداییترین کارها، کمبود امکانات پایهای آن زبانها مانند نداشتن lambda expressions، anonymous methods و anonymous delegates هستند. به همین دلیل مجبورند همیشه و در همهجا از interfaceها استفاده کنند.
ادامه دارد ...
سیر تکاملی delegates را در مثال ساده زیر میتوان ملاحظه کرد:
using System; namespace ActionFuncSamples { public delegate int AddMethodDelegate(int a); public class DelegateSample { public void UseDelegate(AddMethodDelegate addMethod) { Console.WriteLine(addMethod(5)); } } public class Helper { public int CustomAdd(int a) { return ++a; } } class Program { static void Main(string[] args) { Helper helper = new Helper(); // .NET 1 AddMethodDelegate addMethod = new AddMethodDelegate(helper.CustomAdd); new DelegateSample().UseDelegate(addMethod); // .NET 2, anonymous delegates new DelegateSample().UseDelegate(delegate(int a) { return helper.CustomAdd(a); }); // .NET 3.5 new DelegateSample().UseDelegate(a => helper.CustomAdd(a)); } } }
در دات نت یک، یک وهله از شیء AddMethodDelegate ساخته شده و سپس متدی که امضایی متناسب و متناظر با آن را داشت، به عنوان متد انجام دهنده مسئولیت معرفی میشد. در دات نت دو، اندکی نحوه تعریف delegates با ارائه delegates بینام، سادهتر شد و در دات نت سه و نیم با ارائه lambda expressions ، تعریف و استفاده از delegates باز هم سادهتر و زیباتر گردید.
به علاوه در دات نت 3 و نیم، دو Generic delegate به نامهای Action و Func نیز ارائه گردیدهاند که به طور کامل جایگزین تعریف طولانی delegates در کدهای پس از دات نت سه و نیم شدهاند. تفاوتهای این دو نیز بسیار ساده است:
اگر قرار است واگذاری قسمتی از کد را به متدی محول کنید که مقداری را بازگشت میدهد، از Func و اگر این متد خروجی ندارد از Action استفاده نمائید:
Action<int> example1 = x => Console.WriteLine("Write {0}", x); example1(5); Func<int, string> example2 = x => string.Format("{0:n0}", x); Console.WriteLine(example2(5000));
پس از این مقدمه، در ادامه قصد داریم مثالهای دنیای واقعی Action و Func را که در سالهای اخیر بسیار متداول شدهاند، بررسی کنیم.
مثال یک) ساده سازی تعاریف API ارائه شده به استفاده کنندگان از کتابخانههای ما
عنوان شد که کار delegates، واگذاری مسئولیت انجام کاری به کلاسهای دیگر است. این مورد شما را به یاد کاربردهای interfaceها نمیاندازد؟
در interfaceها نیز یک قرارداد کلی تعریف شده و سپس کدهای یک کتابخانه، تنها با امضای متدها و خواص تعریف شده در آن کار میکنند و کتابخانه ما نمیداند که این متدها قرار است چه پیاده سازی خاصی را داشته باشند.
برای نمونه طراحی API زیر را درنظر بگیرید که در آن یک interface جدید تعریف شده که تنها حاوی یک متد است. سپس کلاس Runner از این interface استفاده میکند:
using System; namespace ActionFuncSamples { public interface ISchedule { void Run(); } public class Runner { public void Exceute(ISchedule schedule) { schedule.Run(); } } public class HelloSchedule : ISchedule { public void Run() { Console.WriteLine("Just Run!"); } } class Program { static void Main(string[] args) { new Runner().Exceute(new HelloSchedule()); } } }
نظر شما در مورد این طراحی ساده شده چیست؟
using System; namespace ActionFuncSamples { public class Schedule { public void Exceute(Action run) { run(); } } class Program { static void Main(string[] args) { new Schedule().Exceute(() => Console.WriteLine("Just Run!")); } } }
بدیهی است delegates نمیتوانند به طور کامل جای interfaceها را پر کنند. اگر نیاز است قرارداد تهیه شده بین ما و استفاده کنندگان از کتابخانه، حاوی بیش از یک متد باشد، استفاده از interfaceها بهتر هستند.
از دیدگاه بسیاری از طراحان API، اشیاء delegate معادل interface ایی با یک متد هستند و یک وهله از delegate معادل وهلهای از کلاسی است که یک interface را پیاده سازی کردهاست.
علت استفاده بیش از حد interfaceها در سایر زبانها برای ابتداییترین کارها، کمبود امکانات پایهای آن زبانها مانند نداشتن lambda expressions، anonymous methods و anonymous delegates هستند. به همین دلیل مجبورند همیشه و در همهجا از interfaceها استفاده کنند.
ادامه دارد ...