در ادامه مطالب منتشر شده در رابطه با قابلیتهای جدید سیشارپ 6، در این مطلب به بررسی یکی دیگر از این قابلیتها، با نام Expression-Bodied Members خواهیم پرداخت. در واقع در سیشارپ 6، هدف، سادهسازی سینتکس و افزایش بهرهوری برنامهنویس میباشد. در نسخههای قبلی سیشارپ برای یکسری از اعمال روتین میبایستی روالیهایی را مدام تکرار میکردیم؛ به عنوان مثال در تعریف پراپرتیهای یک کلاس در حالت get-only باید هر بار توسط return مقداری را برگردانیم:
نوشتن پراپرتیهایی همانند FullName منجر به نوشتن خطوط کد اضافهتری خواهد شد، هرچند میتوان این حالت را با برداشتن خطوط اضافی بهبود بخشید:
اما در سیشارپ 6 میتوان آن را توسط expression body به یک خط کاهش داد!
به راحتی میتوانید از سینتکس زیر استفاده نمائید:
به عنوان مثال، میتوان پراپرتی FullName را در کلاس Person با کمک قابلیت expression body به صورت زیر بازنویسی کنیم:
با کد فوق به راحتی توانستیم قسمتهای اضافهای را حذف کنیم. اکنون ممکن است بپرسید آیا این تغییر در performance برنامه تاثیری دارد؟ خیر؛ زیرا سینتکس فوق دقیقاً همان کد ILی را تولید خواهد کرد که در حالت عادی تولید میشود. همچنین delegateی را تولید نخواهد کرد؛ بلکه تنها از سینتکس lambda expression برای خلاصهنویسی بدنه پراپرتی استفاده میکند. در حال حاضر برای حالت setter سینتکسی ارائه نشده است.
میتوانیم کد فوق را به این صورت خلاصهنویسی کنیم: نکته: توجه داشته باشید که در هر دو حالت فوق تنها میتوانیم برای get از expression body استفاده کنیم، هنوز سینتکسی برای حالت set ارائه نشده است.
میتوانیم آن را به صورت زیر بنویسیم:
همانطور که مشاهده میکنید به جای نوشتن curly braces یا {} از lambda arrow یا <= استفاده کردهایم. در اینجا عبارت سمت راست lambda arrow نمایانگر بدنهی متد است. همچنین برای متدهای دارای پارامتر نیز به این صورت عمل میکنیم:
یک عضو از کلاس که به صورت expression body نوشته شده باشد، expression bodied member نامیده میشود. این عضو از کلاس در ظاهر شبیه به عبارات لامبدای ناشناس (anonymous lambda expression) است. اما یک expression bodied member باید دارای نام، مقدار بازگشتی و بدنه متد باشد.
برای حالت فوق به عنوان یک روش جایگزین میتوان از conditional operator استفاده کرد:
را میتوان در حالت expression body به این صورت نوشت:
و یا به این صورت:
برای نوشتن پراپرتیهای فوق به صورت expression body میتوانیم به این صورت عمل کنیم:
اگر ReSharper را نصب کرده باشید، به شما پیشنهاد میدهد که از expression body استفاده نمائید: :
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public string FullName { get { return FirstName + " " + LastName; } } }
public string FullName { get { return FirstName + " " + LastName; } }
استفاده از expression body برای پراپرتیهای get-only (فقط خواندنی):
اگر در کلاسهایتان پراپرتیهای get-only دارید، به راحتی میتوانید بدنهی پراپرتی را با استفاده از expression syntax خلاصهنویسی کنید. در واقع شما با استفاده از سینتکس lambda expression اقدام به نوشتن بدنه پراپرتیهای موردنظرتان میکنید. یعنی به جای نوشتن کدی مانند:
{ get { return your expression; } }
=> your expression;
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public string FullName => FirstName + " " + LastName; }
استفاده از expression body برای Indexerها:
همچنین از این قابلیت برای Indexerها نیز میتوان استفاده کرد، مثلاً به جای نوشتن کد زیر:
public string this[int number] { get { if (number >= 0 && number < _values.Length) { return _values[number]; } return "Error"; } }
public string this[int number] => (number >= 0 && number < _values.Length) ? _values[number] : "Error";
استفاده از expression body برای متدها:
برای متدها نیز میتوانیم از قابلیت عنوان شده استفاده نمائیم، به عنوان مثال اگر داخل کلاس Person متد زیر را داشته باشیم:
public override string ToString() { return FirstName; }
public override string ToString() => FirstName;
public int DoubleTheValue(int someValue) => someValue * 2;
تقریباً تمامی access modifierها در این حالت قابلیت استفاده را دارند. تنها متدهای abstract نمیتوانند استفاده شوند.
محدودیتهای Expression Bodied Members
- یکی از محدودیتهای استفاده از expression body داشتن چندین خط دستور برای بدنه متدهایمان است. در اینحالت باید از روش سابق (statement body) استفاده نمائید.
- یکی دیگر از محدودیتها عدم امکان استفاده از if, else, switch است. به عنوان مثال نمیتوان کد زیر را با داشتن if و else به صورت expression body نوشت:
public override string ToString() { if (MiddleName != null) { return FirstName + " " + MiddleName + " " + LastName; } else { return FirstName + " " + LastName; } }
public override string ToString() => (MiddleName != null) ? FirstName + " " + MiddleName + " " + LastName : FirstName + " " + LastName;
- همچنین نمیتوان از for, foreach, while, do در expression body استفاده کرد، به جای آن میتوان از عبارتهای LINQ برای بدنه تابع استفاده کرد. به عنوان مثال متد زیر:
public IEnumerable<int> SmallNumbers() { for (int i = 0; i < 10; i++) yield return i; }
public IEnumerable<int> SmallNumbers() => from n in Enumerable.Range(0, 10) select n;
public IEnumerable<int> SmallNumbers() => Enumerable.Range(0, 10).Select(n => n);
- همانطور که عنوان شد، استفاده از expression body در قسمت پراپرتیها تنها محدود به پراپرتیهای get-only (فقط خواندنی) میباشد.
- استفاده از این قابلیت برای متدهای سازنده
- استفاده در رخدادها
- استفاده در finalizers
نکته: اگر میخواهید expression bodied member شما هم initializer داشته باشد و همچنین یک read only auto property باشد، باید مقداری سینتکس آن را تغییر دهید. همانطور که میدانید auto propertyها نیازی به backing field ندارند؛ بلکه در زمان کامپایل به صورت خودکار تولید خواهند شد. در نتیجه برای مقداردهی اولیه به backing fieldها میتوانیم درون سازنده کلاس آنها را initialize کنیم:
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public Person() { this.FirstName = "Sirwan"; this.LastName = "Afifi"; } }
public string FirstName { get; set; } = "Sirwan"; public string LastName { get; set; } = "Afifi";
برای حالت فوق:
برای پراپرتیها: