شاید سادهترین تعریف برای Saltarelle این باشد که «کامپایلریست که کدهای C# را به جاوا اسکریپت تبدیل میکند». محاسن زیادی را میتوان برای اینگونه کامپایلرها نام برد؛ مخصوصا در پروژههای سازمانی که نگهداری از کدهای جاوا اسکریپت بسیار سخت و گاهی خارج از توان است و این شاید مهمترین عامل ظهور ابزارهای جدید از قبیل Typescript باشد.
در هر صورت اگر حوصله و وقت کافی برای تجهیز تیم نرم افزاری، به دانش یک زبان جدید مانند Typescript نباشد، استفاده از توان و دانش تیم تولید، از زبان C# سادهترین راه حل است و اگر ابزاری مطمئن برای استفاده از حداکثر قدرت JavaScript همراه با امکانات نگهداری و توسعه کدها وجود داشته باشد، بی شک Saltarelle یکی از بهترینهای آنهاست.
قبلا کامپایلر هایی از این دست مانند Script# وجود داشتند، اما فاقد همه امکانات C# بوده وعملا قدرت کامل C# در کد نویسی وجود نداشت. اما با توجه به ادعای توسعه دهندگان این کامپایلر سورس باز در استفادهی حداکثری از کلیه ویژگیهای C# 5 و با وجود Library های متعدد میتوان Saltarelle را عملا یک کامپایلر موفق در این زمینه دانست.
برای استفاده از Saltarelle در یک برنامه وب ساده باید یک پروژه Console Application به Solution اضافه کرد و پکیج Saltarelle.Compiler را از nuget نصب نمایید. بعد از نصب این پکیج، کلیه Reference ها از پروژه حدف میشوند و هر بار Build توسط کامپایلر Saltarelle انجام میشود. البته با اولین Build، مقداری Error را خواهید دید که برای از بین بردنشان نیاز است پکیج Saltarelle.Runtime را نیز در این پروژه نصب نمایید:
PM> Install-Package Saltarelle.Compiler PM> Install-Package Saltarelle.Runtime
در صورتیکه کماکان Build نهایی با Error همرا بود، یکبار این پروژه را Unload و سپس مجددا Load نمایید
UI یک پروژه وب MVC است و Client یک Console Application که پکیجهای مورد نیاز Saltarelle روی آن نصب شده است.
در صورتیکه پروژه را Build نماییم و نگاهی به پوشهی Debug بیاندازیم، یک فایل JavaScript همنام پروژه وجود دارد:
برای اینکه بعد از هر بار Build ، فایل اسکریپت به پوشهی مربوطه در پروژه UI منتقل شود کافیست کد زیر را در Post Build پروژه Client بنویسیم:
copy "$(TargetDir)$(TargetName).js" "$(SolutionDir)SalratelleSample.UI\Scripts"
اکنون پس از هر بار Build ، فایل اسکریپت مورد نظر در پوشهی Scripts پروژه UI آپدیت میشود:
در ادامه کافیست فایل اسکریپت را به layout اضافه کنیم.
<script src="~/Scripts/SaltarelleSample.Client.js"></script>
در پوشهی Saltarelle.Runtime در پکیجهای نصب شده، یک فایل اسکریپت به نام mscorlib.min.js نیز وجود دارد که حاوی اسکریپتهای مورد نیاز Saltarelle در هنگام اجراست. آن را به پوشه اسکریپتهای پروژه UI کپی نمایید و سپس به Layout اضافه کنید.
<script src="~/Scripts/mscorlib.min.js"></script> <script src="~/Scripts/SaltarelleSample.Client.js"></script>
حال نوبت به اضافه نمودن libraryهای مورد نیازمان است. برای دسترسی به آبجکت هایی از قبیل document, window, element و غیره در جاوااسکریپت میتوان پکیج Saltarelle.Web را در پروژهی Client نصب نمود و برای دسترسی به اشیاء و فرمانهای jQuery، پکیج Salratelle.jQuery را نصب نمایید.
> Install-Package Saltarelle.Web > Install-Package Saltarelle.jQuery
به این libraryها imported library میگویند. در واقع، در زمان کامپایل، برای این libraryها فایل اسکریپتی تولید نمیشود و فقط آبجکتهای #C هستند که که هنگام کامپایل تبدیل به کدهای ساده اسکریپت میشوند که اگر اسکریپت مربوط به آنها به صفحه اضافه نشده باشد، اجرای اسکریپت با خطا مواجه میشود.
به طور سادهتر وقتی از jQuery library استفاده میکنید هیچ فایل اسکریپت اضافهای تولید نمیشود، اما باید اسکریپت jQuery به صفحه شما اضافه شده باشد.
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
مثال ما یک اپلیکیشن ساده برای خواندن فیدهای همین سایت است. ابتدا کدهای سمت سرور را در پروژه UI می نویسیم.
کلاسهای مورد نیاز ما برای این فید ریدر:
public class Feed { public string FeedId { get; set; } public string Title { get; set; } public string Address { get; set; } } public class Item { public string Title { get; set; } public string Link { get; set; } public string Description { get; set; } }
و یک کلاس برای مدیریت منطق برنامه
public class SiteManager { private static List<Feed> _feeds; public static List<Feed> Feeds { get { if (_feeds == null) _feeds = CreateSites(); return _feeds; } } private static List<Feed> CreateSites() { return new List<Feed>() { new Feed(){ FeedId = "1", Title = "آخرین تغییرات سایت", Address = "https://www.dntips.ir/rss.xml" }, new Feed(){ FeedId = "2", Title = "مطالب سایت", Address = "https://www.dntips.ir/feeds/posts" }, new Feed(){ FeedId = "3", Title = "نظرات سایت", Address = "https://www.dntips.ir/feeds/comments" }, new Feed(){ FeedId = "4", Title = "خلاصه اشتراک ها", Address = "https://www.dntips.ir/feed/news" }, }; } public static IEnumerable<Item> GetNews(string id) { XDocument feedXML = XDocument.Load(Feeds.Find(s=> s.FeedId == id).Address); var feeds = from feed in feedXML.Descendants("item") select new Item { Title = feed.Element("title").Value, Link = feed.Element("link").Value, Description = feed.Element("description").Value }; return feeds; } }
کلاس SiteManager فقط یک لیست از فیدها دارد و متدی که با گرفتن شناسهی فید ، یک لیست از آیتمهای موجود در آن فید ایجاد میکند.
حال دو ApiController برای دریافت دادهها ایجاد میکنیم
public class FeedController : ApiController { // GET api/<controller> public IEnumerable<Feed> Get() { return SiteManager.Feeds; } } public class ItemsController : ApiController { // GET api/<controller>/5 public IEnumerable<Item> Get(string id) { return SiteManager.GetNews(id); } }
در View پیشفرض که Index از کنترلر Home است، یک Html ساده برای
فرم صفحه اضافه میکنیم
<div> <div> <h2>Feeds</h2> <ul id="Feeds"> </ul> </div> <div> <h2>Items</h2> <p id="FeedItems"> </p> </div> </div>
در المنت Feeds لیست فیدها را قرار میدهیم و در FeedItems آیتمهای مربوط به هر فید. حال به سراغ کدهای سمت کلاینت میرویم و به جای جاوا اسکریپت از Saltarelle استفاده میکنیم.
کلاس Program را از پروژه Client باز میکنیم و متد Main را به شکل زیر تغییر میدهیم:
static void Main() { jQuery.OnDocumentReady(() => { FillFeeds(); }); }
بعد از کامپایل شدن، کد #C شارپ بالا به صورت زیر در میآید:
$SaltarelleSample_Client_$Program.$main = function() { $(function() { $SaltarelleSample_Client_$Program.$fillFeeds(); }); }; $SaltarelleSample_Client_$Program.$main();
و این همان متد معروف jQuery است که Saltarelle.jQuery برایمان ایجاد کرده است.
متد FillFeeds را به شکل زیر پیاده سازی میکنیم
private static void FillFeeds() { jQuery.Ajax(new jQueryAjaxOptions() { Url = "/api/feed", Type = "GET", Success = (d,t,r) => { // Fill var ul = jQuery.Select("#Feeds"); jQuery.Each((List<Feed>)d, (idx,i) => { var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer"); li.Click(eve => { FillData(i.FeedId); }); ul.Append(li); }); } }); }
آبجکت jQuery، متدی به نام Ajax دارد که یک شی از کلاس jQueryAjaxOptions را به عنوان پارامتر میپذیرد. این کلاس کلیه خصوصیات متد Ajax در jQuery را پیاده سازی میکند. نکته شیرین آن توانایی نوشتن lambda برای Delegate هاست.
خاصیت Success یک Delegate است که 3 پارامتر ورودی را میپذیرد.
public delegate void AjaxRequestCallback(object data, string textStatus, jQueryXmlHttpRequest request);
data همان مقداریست که api باز میگرداند که یک لیست از Feed هاست. برای زیبایی کار، من یک کلاس Feed در پروژه Client اضافه میکنم که خصوصیاتی مشترک با کلاس اصلی سمت سرور دارد و مقدار برگشی Ajax را به آن تبدیل میکنم.
کلاس Feed و Item
[PreserveMemberCase()] public class Feed { //[ScriptName("FeedId")] public string FeedId; //[ScriptName("Title")] public string Title; //[ScriptName("Address")] public string Address; } [PreserveMemberCase()] public class Item { // [ScriptName("Title")] public string Title; // [ScriptName("Link")] public string Link; // [ScriptName("Description")] public string Description; }
jQuery.Each((List<Feed>)d, (idx,i) => { var li = jQuery.Select("<li>").Text(i.Title).CSS("cursor", "pointer"); li.Click(eve => { FillData(i.FeedId); }); ul.Append(li); });
به ازای هر آیتمی که در شیء بازگشتی وجود دارد، با استفاد از متد each در jQuery یک li ایجاد میکنیم. همان طور که میبینید کلیه خواص، به شکل Fluent قابل اضافه شدن میباشد. سپس برای li یک رویداد کلیک که در صورت وقوع، متد FillData را با شناسه فید کلیک شده فراخوانی میکند و در آخر li را به المنت ul اضافه میکنیم.
برای هر کلیک هم مانند مثال بالا api را با شناسهی فید مربوطه فراخوانی کرده و به ازای هر آیتم، یک سطر ایجاد میکنیم.
private static void FillData(string p) { jQuery.Ajax(new jQueryAjaxOptions() { Url = "/api/items/" + p, Type = "GET", Success = (d, t, r) => { var content = jQuery.Select("#FeedItems"); content.Html(""); foreach (var item in (List<Item>)d) { var row = jQuery.Select("<div>").AddClass("row").CSS("direction", "rtl"); var link = jQuery.Select("<a>").Attribute("href", item.Link).Text(item.Title); row.Append(link); content.Append(row); } } }); }
در این مثال ما از Saltarelle.jQuery برای استفاده از jQuery.js استفاده نمودیم. libraryهای متعددی برای Saltarelle از قبیل linq,angular,knockout,jQueryUI,nodeJs ایجاد شده و همچنین قابلیتهای زیادی برای نوشتن imported libraryهای سفارشی نیز وجود دارد.
مطمئنا استفاده از چنین کامپایلرهایی راه حلی سریع برای رهایی از مشکلات متعدد کد نویسی با جاوا اسکریپت در نرم افزارهای بزرگ مقیاس است. اما مقایسه آنها با ابزارهایی از قبیل typescript احتیاج به زمان و تجربه کافی در این زمینه دارد.
آشنایی با مفهوم Indexer در C#.NET
در حالت عادی میشود بر روی یک وهله از کلاس، Index اعمال کرد. اگر نخواهیم روی کل وهله اعمال شود چطور؟ برای مثال فقط روی یک خاصیت خاص.
پیاده سازی آن یک نکته کوچک دارد که به شرح زیر است:
using System; namespace IndexedProperties { public class Data { private int[] _localArray; private ArrayIndexer _arrayIndexer; public Data() { _localArray = new int[10]; for (int i = 0; i < 10; i++) _localArray[i] = i + 1; _arrayIndexer = new ArrayIndexer(this); } public ArrayIndexer Number { get { return _arrayIndexer; } } public class ArrayIndexer { private Data _arrayOwner; public ArrayIndexer(Data arrayOwner) { _arrayOwner = arrayOwner; } public int this[int index] { get { return _arrayOwner._localArray[index]; } } public int Length { get { return _arrayOwner._localArray.Length; } } } } class Program { static void Main(string[] args) { var data = new Data(); for (int i = 0; i < 10; i++) Console.WriteLine(data.Number[i]); } } }
به این ترتیب تعاریف آرایه مورد استفاده نیازی نیست مستقیما در معرض دید قرار گیرد و همچنین read-only هم تعریف شده است. به علاوه اینکه indexerها محدود به int نیستند و برای مثال میتوان از string و غیره نیز استفاده کرد.
بطور خلاصه در پوشهی obj، ما به ازای هر سورس فایل، یک فایل کامپایل شده داریم؛ اما در پوشهی bin تنها یک فایل یکپارچه شده را خواهیم داشت.
برای این کار از دادههای موجود در پایگاه داده [AdventureWorksLT2008R2].[SalesLT].[Address] استفاده میکنم .
نمونه خروجی مانند زیر میباشد :
سپس برای تنظیمات Script Component به ترتیب زیر عمل میکنیم :
در قسمت Input column ستون هایی را که به عنوان پارامتر میتوانیم با آنها کار کنیم ، تعریف میکنیم : (دقت شود که تغییر نام متغیرها ، در کدها اعمال میشوند .)
حال میخواهم ستونهای تاریخ میلادی و شمسی را برای خروجی تعریف کنم :
همانطور که مشاهده میکنید ، نوع داده ای برای خروجی را رشته تعریف کردم.
سپس به قسمت script بر میگردیم (سمت چپ پنجره ) و روی Edit Script کلیک میکنیم : (در صورتی که تمایل به کد نویسی با VB را دارید در همین قسمت میتوانید آن را تنظیم کنید)
پنجره ای مانند زیر برای ویرایش کدها ، باز میشود
همانطور که مشاهده میکنید درون این کلاس 4 متد تبدیل تاریخ را پیاده کردم و از آنها در متد input0_processInputRow استفاده کردم . این کار نیازی به پیاده سازی حلقه ندارد و به راحتی میتوان آنها را روی سطرهای دلخواه تعریف کرد . خروجی نمایش داده شده در data viewerها مانند زیر میباشند :
قبل از اعمال تبدیلات :
بعد از اعمال تبدیلات :
- لیست زبانهای برنامه نویسی محبوب در ماه آوریل 2009؛ سی شارپ یک پله صعود نسبت به سال قبل، زبان برنامه نویسی MATLAB مجددا وارد لیست شده
- اولین محصول نگارش 2010 مایکروسافت برای دریافت، exchange server 2010 beta (میل سرور)
سایر اولین سری محصولات 2010 ، شامل Microsoft Office 2010 ، Microsoft Visio 2010 و Microsoft Project 2010 خواهند بود.
- مجموعه انتشارات مایکروسافت 25 امین سالگرد تاسیس خود را جشن میگیرد. به همین منظور دو کتاب زیر را به رایگان برای دریافت قرار دادهاند:
عنوانی رو که براش انتخاب کردن جالب است "the world's most advanced open source database"
EF Code First #11
- طراحی Repository اصلی خروجی و ورودی IQueryable ندارد با این صحبت کاملاً موافق هستم و غیر از این هم نیست.
- همانطور که عرض کردم این موضوع کاملاً قابل اجراست. شما به ازاء هر ORM یک DataAccess خواهید داشت و امکانات قوی هر ORM را در داخل DataAccess خودش به راحتی استفاده میکنید به لایه بندی و ارجاعاتی که عرض کردم دقت بفرمایید. مصرف کننده Data به جای استفاده از DataAccess های Concrete از سطوح بالاتری استفاده میکنند و DI این موضوع را حل کرده است. شما میتوانید از هر امکانی که دوست دارید و مخصوص ORM خودتان است در آن استفاده کنید.
- لایه سرویس با لایه Data متفاوت است. با چیزی که شما فرمودید اگر ORM تغییر کند باید کدهای لایه سرویس را دستی تغییر دهید و با Breaking Change روبرو میشویم. اما همانطور که قبلاً هم گفتم، با وجود این Abstraction که گفتم، فقط با یک Config ساده در DI این موضوع به طور کامل حل میشود. کلاً فلسفه وجودی Repository همین ورود و خروج داده (ذخیره و بازیابی) و Abstract بودن آنهاست. کدهای ORM نه تنها نباید در Code Behinde ها دیده شود بلکه نباید در Service یا Business سیستم هم باشند. به همان دلیلی که عرض کردم. و البته مثال رمزنگاری مثال خوبی است (Service چرا باید درگیر رمزنکاری شود؟ این وظیفه لایه ذخیره و بازیابی است که همان Repository خواهد بود).
- http://martinfowler.com/eaaCatalog/repository.html Repository همانطور که از نامش پیداست یک مخزن است که ما نمیدانید اطلاعات را در کجا نگهداری خواهد کرد (حافظه، بانک و ...) حال در یک پروژه این محل نگهداری باید امن باشد همین! به جای حافظه و بانک و ... تصور کنید یک جای امن را. آیا برای این نمیتوان (نباید) یک Repository ساخت؟ مورد بعدی اینکه رمزنگاری به لایه دیگری منتقل شده و Repository که گفتم از همان استفاده میکند. نکته بعدی اینکه با چیزی که شما فرمودید، باید مراقبت از اینکه همه چیز رمزنگاری شود را به برنامه نویس محول کرد و حتماً حواسش باشد که فراموش نکند قبل از ذخیره یا بازیابی یک Entity متدهای لازم برای رمزنگاری را فراخوانی کند. در صورتی که لایه سرویس باید متمرکز بر روی Business کار باشد نه فراخوانی متدهای رمزنگاری برای ذخیره سازی و ...
تعدادی از فناوریهایی که توسط این کتابخانه پشتیبانی میشوند در زیر آمده است:
Web Service: این فناوری اجازهی ارسال و دریافت پیامهای تحت شبکه را به خصوص بر روی اینترنت، فراهم میکند و باعث ارتباط جامعتر بین برنامهها و فناوریهای مختلف میگردد. در انواع جدیدتر WCF و Web Api نیز به بازار ارائه شدهاند.
webform و MVC : فناوریهای تحت وب که باعث سهولت در ساخت وب سایتها میشوند که وب فرم رفته رفته به سمت منسوخ شدن پیش میرود و در صورتی که قصد دارید طراحی وب را آغاز کنید توصیه میکنم از همان اول به سمت MVC بروید.
Rich Windows GUI Application : برای سهولت در ایجاد برنامههای تحت وب حالا چه با فناوری WPF یا فناوری قدیمی و البته منسوخ شده Windows Form.
Windows Console Application: برای ایجاد برنامههای ساده و بدون رابط گرافیکی.
Windows Services: شما میتوانید یک یا چند سرویس تحت ویندوز را که توسط Service Control Manager یا به اختصار SCM کنترل میشوند، تولید کنید.
Database stored Procedure: نوشتن stored procedure بر روی دیتابیسهایی چون sql server و اوراکل و ... توسط فریم ورک دات نت مهیاست.
Component Libraray: ساخت اسمبلیهای واحدی که میتوانند با انواع مختلفی از موارد بالا ارتباط برقرار کنند.
Portable Class Libary : این نوع پروژهها شما را قادر میسازد تا کلاسهایی با قابلیت انتقال پذیری برای استفاده در سیلور لایت، ویندوز فون و ایکس باکس و فروشگاه ویندوز و ... تولید کنید.
ازآنجا که یک کتابخانه شامل زیادی نوع میگردد سعی شده است گروه بندیهای مختلفی از آن در قالبی به اسم فضای نام namespace تقسیم بندی گردند که شما آشنایی با آنها دارید. به همین جهت فقط تصویر زیر را که نمایشی از فضای نامهای اساسی و مشترک و پرکاربرد هستند، قرار میدهم.
در CLR مفهومی به نام Common Type System یا CTS وجود دارد که توضیح میدهد نوعها باید چگونه تعریف شوند و چگونه باید رفتار کنند که این قوانین از آنجایی که در ریشهی CLR نهفته است، بین تمامی زبانهای دات نت مشترک میباشد. تعدادی از مشخصات این CTS در زیر آورده شده است ولی در آینده بررسی بیشتری روی آنان خواهیم داشت:
- فیلد
- متد
- پراپرتی
- رویدادها
CTS همچنین شامل قوانین زیادی در مورد وضعیت کپسوله سازی برای اعضای یک نوع دارد:
- private
- public
- Family یا در زبانهایی مثل سی ++ و سی شارپ با نام protected شناخته میشود.
- family and assembly: این هم مثل بالایی است ولی کلاس مشتق شده باید در همان اسمبلی باشد. در زبانهایی چون سی شارپ و ویژوال بیسیک، چنین امکانی پیاده سازی نشدهاست و دسترسی به آن ممکن نیست ولی در IL Assembly چنین قابلیتی وجود دارد.
- Assembly یا در بعضی زبانها به نام internal شناخته میشود.
- Family Or Assembly: که در سی شارپ با نوع Protected internal شناخته میشود. در این وضعیت هر عضوی در هر اسمبلی قابل ارث بری است و یک عضو فقط میتواند در همان اسمبلی مورد استفاده قرار بگیرد.
موارد دیگری که تحت قوانین CTS هستند مفاهیم ارث بری، متدهای مجازی، عمر اشیاء و .. است.
یکی دیگر از ویژگیهای CTS این است که همهی نوعها از نوع شیء Object که در فضای نام system قرار دارد ارث بری کردهاند. به همین دلیل همهی نوعها حداقل قابلیتهایی را که یک نوع object ارئه میدهد، دارند که به شرح زیر هستند:
- مقایسهی دو شیء از لحاظ برابری.
- به دست آوردن هش کد برای هر نمونه از یک شیء
- ارائهای از وضعیت شیء به صورت رشته ای
- دریافت نوع شیء جاری
وجود COMها به دلیل ایجاد اشیاء در یک زبان متفاوت بود تا با زبان دیگر ارتباط برقرار کنند. در طرف دیگر CLR هم بین زبانهای برنامه نویسی یکپارچگی ایجاد کرده است. یکپارچگی زبانهای برنامه نویسی علل زیادی دارند. اول اینکه رسیدن به هدف یا یک الگوریتم خاص در زبان دیگر راحتتر از زبان پایه پروژه است. دوم در یک کار تیمی که افراد مختلف با دانش متفاوتی حضور دارند و ممکن است زیان هر یک متفاوت باشند.
برای ایجاد این یکپارچگی، مایکروسافت سیستم CLS یا Common Language Specification را راه اندازی کرد. این سیستم برای تولیدکنندگان کامپایلرها جزئیاتی را تعریف میکند که کامپایلر آنها را باید با حداقل ویژگیهای تعریف شدهی CLR، پشتیبانی کند.
CLR/CTS مجموعهای از ویژگیها را شامل میشود و گفتیم که هر زبانی بسیاری از این ویژگیها را پشتیبانی میکند ولی نه کامل. به عنوان مثال برنامه نویسی که قصد کرده از IL Assembly استفاده کند، قادر است از تمامی این ویژگیهایی که CLR/CTS ارائه میدهند، استفاده کند ولی تعدادی دیگر از زبانها مثل سی شارپ و فورترن و ویژوال بیسیک تنها بخشی از آن را استفاده میکنند و CLS حداقل ویژگی که بین همه این زبانها مشترک است را ارائه میکند.
شکل زیر را نگاه کنید:
یعنی
اگر شما دارید نوع جدیدی را در یک زبان ایجاد میکنید که قصد دارید در یک
زبان دیگر استفاده شود، نباید از امتیازات ویژهای که آن زبان در اختیار شما میگذارد و به بیان بهتر CLS آنها را پشتیبانی نمیکند، استفاده کنید؛ چرا
که کد شما ممکن است در زبان دیگر مورد استفاده قرار نگیرد.
using System; // Tell compiler to check for CLS compliance [assembly: CLSCompliant(true)] namespace SomeLibrary { // Warnings appear because the class is public public sealed class SomeLibraryType { // Warning: Return type of 'SomeLibrary.SomeLibraryType.Abc()' // is not CLScompliant public UInt32 Abc() { return 0; } // Warning: Identifier 'SomeLibrary.SomeLibraryType.abc()' // differing only in case is not CLScompliant public void abc() { } // No warning: this method is private private UInt32 ABC() { return 0; } } }
دومین اخطار اینکه دو متد یکسان وجود دارند که در حروف بزرگ و کوچک تفاوت دارند. ولی زبان هایی چون ویژوال بیسیک نمیتوانند تفاوتی بین دو متد abc و ABC بیابند.
نکتهی جالب اینکه اگر شما کلمه public را از جلوی نام کلاس بردارید تمامی این اخطارها لغو میشود. به این خاطر که اینها اشیای داخلی آن اسمبلی شناخته شده و قرار نیست از بیرون به آن دسترسی صورت بگیرد. عضو خصوصی کد بالا را ببینید؛ کامنت بالای آن میگوید که چون خصوصی است هشداری نمیگیرد، چون قرار نیست در زبان مقصد از آن به طور مستقیم استفاده کند.
برای دیدن قوانین CLS به این صفحه مراجعه فرمایید.
در بالا در مورد یکپارچگی و سازگاری کدهای مدیریت شده توسط CLS صحبت کردیم ولی در مورد ارتباط با کدهای مدیریت نشده چطور؟
مایکروسافت موقعیکه CLR را ارئه کرد، متوجه این قضیه بود که بسیاری از شرکتها توانایی اینکه کدهای خودشون را مجددا طراحی و پیاده سازی کنند، ندارند و خوب، سورسهای مدیریت نشدهی زیادی هم موجود هست که توسعه دهندگان علاقه زیادی به استفاده از آنها دارند. در نتیجه مایکروسافت طرحی را ریخت که CLR هر دو قسمت کدهای مدیریت شده و نشده را پشتیبانی کند. دو نمونه از این پشتیبانی را در زیر بیان میکنیم:
یک. کدهای مدیریت شده میتوانند توابع مدیریت شده را در قالب یک dll صدا زده و از آنها استفاده کنند.
دو. کدهای مدیریت شده میتوانند از کامپوننتهای COM استفاده کنند: بسیاری از شرکتها از قبل بسیاری از کامپوننتهای COM را ایجاد کرده بودند که کدهای مدیریت شده با راحتی با آنها ارتباط برقرار میکنند. ولی اگر دوست دارید روی آنها کنترل بیشتری داشته باشید و آن کدها را به معادل CLR تبدیل کنید؛ میتوانید از ابزار کمکی که مایکروسافت همراه فریم ورک دات نت ارائه کرده است استفاده کنید. نام این ابزار TLBIMP.exe میباشد که از Type Library Importer گرفته شده است.
سه. اگر کدهای مدیریت نشدهی زیادتری دارید شاید راحتتر باشد که برعکس کار کنید و کدهای مدیریت شده را در در یک برنامهی مدیریت نشده اجرا کنید. این کدها میتوانند برای مثال به یک Activex یا shell Extension تبدیل شده و مورد استفاده قرار گیرند. ابزارهای TLBEXP .exe و RegAsm .exe برای این منظور به همراه فریم ورک دات نت عرضه شده اند.
سورس کد Type Library Importer را میتوانید در کدپلکس بیابید.
در ویندوز 8 به بعد مایکروسافت API جدید را تحت عنوان WinsowsRuntime یا winRT ارائه کرده است . این api یک سیستم داخلی را از طریق کامپوننتهای com ایجاد کرده و به جای استفاده از فایلهای کتابخانهای، کامپوننتها api هایشان را از طریق متادیتاهایی بر اساس استاندارد ECMA که توسط تیم دات نت طراحی شده است معرفی میکنند.
زیبایی این روش اینست که کد نوشته شده در زبانهای دات نت میتواند به طور مداوم با apiهای winrt ارتباط برقرار کند. یعنی همهی کارها توسط CLR انجام میگیرد بدون اینکه لازم باشد از ابزار اضافی استفاده کنید. در آینده در مورد winRT بیشتر صحبت میکنیم.
سخن پایانی: ممنون از دوستان عزیز بابت پیگیری مطالب تا بدینجا. تا این قسمت فصل اول کتاب با عنوان اصول اولیه CLR بخش اول مدل اجرای CLR به پایان رسید.
ادامهی مطالب بعد از تکمیل هر بخش در دسترس دوستان قرار خواهد گرفت.
If Not a And b Then ... Else ... EndIf
if(!(a) && b) { ... } else { }
معرفی واژهی کلیدی جدید not در C# 9.0
در ابتدا اینترفیس نمونهای را به همراه دو کلاس مشتق شدهی از آن درنظر بگیرید:
public interface ICommand { } public class Command1 : ICommand { } public class Command2 : ICommand { }
ICommand command = new Command1(); if (!(command is Command2)) { }
if (command is not Command2) { }
معرفی واژههای کلیدی جدید and و or در C# 9.0
واژههای کلیدی جدید and و or نیز درک و نوشتن عبارات pattern matching را بسیار ساده میکنند. برای نمونه قطعه کد متداول زیر را درنظر بگیرید:
if ((command is ICommand) && !(command is Command2)) { }
if (command is ICommand and not Command2) { }
و یا حتی در اینجا در صورت نیاز میتوان از واژهی کلیدی جدید or نیز استفاده کرد:
if (command is Command1 or Command2) { }
امکان اعمال واژههای کلیدی جدید and، or و not به سایر نوعها نیز وجود دارند
تا اینجا مثالهایی را که بررسی کردیم، در مورد بررسی نوع اشیاء بود. اما میتوان این واژههای کلیدی جدید در C# 9.0 را به هر نوع ممکنی نیز اعمال کرد. برای نمونه، مثال سادهی زیر را که در مورد بررسی اعداد است، درنظر بگیرید:
var number = new Random().Next(1, 10); if (number > 2 && number < 8) { }
if (number is > 2 and < 8) { }
یک مثال دیگر: متد زیر را در نظربگیرید که با استفاده از && و || متداول #C نوشته شدهاست:
public static bool IsLetterOrSeparator(char c) => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '.' || c == ',';
public static bool IsLetterOrSeparator(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';
امکان اعمال واژههای کلیدی جدید and، or و not به switchها نیز وجود دارد
برای نمونه قطعه کد if/else دار متداول زیر را درنظر بگیرید که قابلیت تبدیل به یک سوئیچ را نیز دارد:
var number = new Random().Next(1, 10); if (number <= 0) { Console.WriteLine("Less than or equal to 0"); } else if (number > 0 && number <= 10) { Console.WriteLine("More than 0 but less than or equal to 10"); } else { Console.WriteLine("More than 10"); }
// C#9.0 switch (number) { case <= 0: Console.WriteLine("Less than or equal to 0"); break; case > 0 and <= 10: Console.WriteLine("More than 0 but less than or equal to 10"); break; default: Console.WriteLine("More than 10"); break; }
// C#7.0 switch (number) { case int value when value <= 0: Console.WriteLine("Less than or equal to 0"); break; case int value when value > 0 && value <= 10: Console.WriteLine("More than 0 but less than or equal to 10"); break; default: Console.WriteLine("More than 10"); break; }
و یا حتی میتوان سوئیچ C# 9.0 را توسط switch expression بهبود یافتهی C# 8.0 نیز به شکل زیر بازنویسی کرد:
var message = number switch { <= 0 => "Less than or equal to 0", > 0 and <= 10 => "More than 0 but less than or equal to 10", _ => "More than 10" };
انواع pattern matchingهای اضافه شدهی به C# 9.0
در این مطلب سعی شد مفاهیم pattern matching اضافه شدهی به C# 9.0، ذیل عنوان واژههای کلیدی جدید آن بحث شوند؛ اما هر کدام دارای نامهای خاصی هم هستند:
الف) relational patterns: امکان استفادهی از <, >, <= and >= را در الگوها میسر میکنند. مانند نمونههای سوئیچی که نوشته شد.
ب) logical patterns: امکان استفادهی از واژههای کلیدی and، or و not را در الگوها ممکن میکنند.
ج) not pattern: امکان استفادهی از واژهی کلیدی not را در عبارات if میسر میکند.
د) Simple type pattern: در مثالهای زیر، پس از انطباق با یک الگو، کاری با متغیر یا شیء مرتبط نداریم. در نگارشهای قبلی برای صرفنظر کردن از آن، ذکر _ ضروری بود؛ اما در C#9.0 میتوان آنرا نیز ذکر نکرد:
private static int GetDiscount(Product p) => p switch { Food => 0, // Food _ => 0 before C# 9 Book b => 75, // Book b _ => 75 before C# 9 _ => 25 };
Value Types ارجاعی در C# 7.2
در انتهای نکتهی خروجی ref readonly عنوان شد که «در ابتدا قصد داشتند ref readonly را برای تعریف پارامترهای value type نیز بکار برند، اما این تصمیم با معرفی پارامترهای از نوع in جایگزین شد» اما ... مجددا به C# 12 اضافه شدهاست:
مثال زیر را درنظر بگیرید:
namespace CS8Tests; public class RefReadonlySample { public void Test() { var number = 5; Print(ref number); Console.WriteLine($"After Print -> Your number is {number}"); // Output: // Print -> Your number is 5 // After Print -> Your number is 6 } private void Print(ref int number) { Console.WriteLine($"Print -> Your number is {number}"); number++; } }
اگر بخواهیم از تغییرات پارامتر number در متد Print جلوگیری کنیم، میتوان از واژهی کلیدی in که در C# 7.2 ارائه شد، استفاده کرد:
private void Print(in int number)
error CS8331: Cannot assign to variable 'number' or use it as the right hand side of a ref assignment because it is a readonly variable
اکنون در C# 12 همین عمل را توسط واژههای کلیدی ref readonly نیز میتوان پیاده سازی کرد:
private void Print(ref readonly int number)
سؤال: چرا این تغییر در C# 12 رخ دادهاست، زمانیکه واژهی کلیدی in، دقیقا همین کار را انجام میداد؟
هدف، وضوح بیشتر API تولیدی و تاکید بر readonly بودن ارجاع دریافتی در این حالت و یکدستی قسمتهای مختلف زبان است.
همچنین واقعیت این است که یک چنین قابلیتهایی، استفادهی روزمرهای را در زبان #C ندارند و بیشتر هدف از وجود آنها، استفاده از API کتابخانههای C++/C در زبان #C است. برای مثال بجای اینکه تمام ارجاعات فقط خواندنی آنها را به پارامترهایی از نوع in تبدیل کنند (در کدهای قدیمی) که سبب بروز مشکلات عدم سازگاری میشود، اکنون میتوانند به سادگی refهای قدیمی تعریف شده را ref readonly کنند؛ بدون اینکه استفاده کنندگان با مشکلی مواجه شوند.