10 ویژگی جدید C# 7.0 در Visual Studio 2017
بسم الله الرحمن الرحیم
با سلام و وقت بخیر...
چند وقتیست که نسخه جدید #c یعنی c# 7.0 معرفی شده است. شما نیز عقب نمانید!
در این مقاله با 10 مورد از ویژگی های c# 7.0 و کاربرد آن در ویژوال استودیو آشنا خواهید شد. بنابراین تا پایان این مقاله با ما همراه باشید.
بسم الله الرحمن الرحیم
با سلام و وقت بخیر...
چند وقتیست که نسخه جدید #c یعنی c# 7.0 معرفی شده است. شما نیز عقب نمانید!
در این مقاله با 10 مورد از ویژگی های c# 7.0 و کاربرد آن در ویژوال استودیو آشنا خواهید شد. بنابراین تا پایان این مقاله با ما همراه باشید.
اکثر شما قبلاً متوجه شده اید که Visual Studio 2017 RC منتشر شده است و با بسیاری از ویژگی های جدید همراه است. در این مقاله، من فقط درباره ویژگی های C# صحبت خواهم کرد. ویژگی های دیگر در مقالات آینده من توضیح داده خواهد شد.
در C # 7، بسیاری از ویژگی های جدید معرفی شده است. در زیر لیستی از ویژگی هایی است که در این مقاله توضیح خواهم داد. با ما همراه باشید.
- Local functions or nested functions ( توابع محلی یا توابع توزیع شده)
- Binary Literal (استفاده از الفبای دودویی)
- Digit Separators (استفاده از عددهای بزرگ و دیفرانسیلی)
- Pattern matching (پشتیبانی از الگو و قالب های مختلف)
- Out Variables (محیطی برای متغیرهای بیرونی)
- Tuples (Tuples Enhancement) (ایجاد محیطی برای Tuples)
- Deconstruction (splitting tuples) (استفاده جداگانه از tuples)
- Expression Bodied Members (some more expression bodied members in C# 7) (برگرداندن یک مقدار یا عبارت)
- Expression bodied Methods (C# 6.0)
- Expression bodied Properties (C# 6.0)
- Expression bodied constructor (C# 7.0)
- Expression bodied destructor (C# 7.0)
- Expression bodied getters (C# 7.0)
- Expression bodied setters (C# 7.0)
- Ref returns and locals (برگرداندن انواع متغیرها و صدا زدن متدها)
- Throw Expressions (بررسی انواع خطاها با استفاده از Throw)
برخی از ویژگی ها هنوز در Visual Studio 2017 RC پشتیبانی نمی شوند و هنوز در حال توسعه هستند. حتی C # 7 به طور کامل توسعه داده نشده است و هنوز هم در حالت پیش نمایش است. شما می توانید برخی از ویژگی های C # 7 را در حال حاضر نادیده بگیرید و تمرکز خود را بر ویژگی هایی که می توانید در ویژوال استودیو 2017 RC داشته باشید، آزمایش کنید. در زیر تصویری از ویژگی های اصلی C # 7 است که در Visual Studio 2017 RC پشتیبانی می شوند.
Local functions or nested functions
اگر شما بخواهید از چند تابع داخلی در برنامه تان استفاده کنید، به طور کلی آن ها را توابع محلی می نامند. برای درک چگونگی عملکرد تابع محلی، فقط به کد پایین نگاه کنید و ببینید چگونه با متدهای Add() و Multiply() می توانید کار کنید.
مثال اول
using static System.Console;
namespace UseLocalFunctions {
class Program {
static void Main(string[] args) {
void Add(int x, int y) {
WriteLine($ "Sum of {x} and {y} is : {x + y}");
}
void Multiply(int x, int y) {
WriteLine($ "Multiply of {x} and {y} is : {x * y}");
Add(30, 10);
}
Add(20, 50);
Multiply(20, 50);
}
}
}
در مثال فوق، از دو توابع توزیع شده Add() و Multiply() استفاده کردم و می توانید ببینید که آنها را می توان از هر جا درون فراخوانی کرد. هر چند من، تابع Add() را در داخل یک تابع inline دیگر فراخوانی کردم.
مثال دوم
در مثال اول، شما متوجه شده اید که عملکرد توابع توزیع شده چگونه است و هیچگونه محدودیتی وجود ندارد و می تواند هر نوع دیگری را نیز بازگرداند. در این مثال، من قصد دارم یک تابع محلی "CalculateMarks" ایجاد کنم که علامت کل را محاسبه کرده و آن را به عنوان یک دیتا دسیمال نشان می دهد.
public static void PrintStudentMarks(int studentId, params Subject[] subjects) {
WriteLine($ "Student Id {studentId} Total Marks: {CalculateMarks()}");
WriteLine($ "Subject wise marks");
foreach(var subject in subjects) {
WriteLine($ "Subject Name: {subject.SubjectName} \t Marks: {subject.Marks}");
}
decimal CalculateMarks() {
decimal totalMarks = 0;
foreach(var subject in subjects) {
totalMarks += subject.Marks;
}
return totalMarks;
}
}
کد کامل
class Program {
static void Main(string[] args) {
PrintStudentMarks(101, new Subject {
SubjectName = "Math", Marks = 96
}, new Subject {
SubjectName = "physics", Marks = 88
}, new Subject {
SubjectName = "Chem", Marks = 91
});
}
public static void PrintStudentMarks(int studentId, params Subject[] subjects) {
WriteLine($ "Student Id {studentId} Total Marks: {CalculateMarks()}");
WriteLine($ "Subject wise marks");
foreach(var subject in subjects) {
WriteLine($ "Subject Name: {subject.SubjectName} \t Marks: {subject.Marks}");
}
decimal CalculateMarks() {
decimal totalMarks = 0;
foreach(var subject in subjects) {
totalMarks += subject.Marks;
}
return totalMarks;
}
}
public class Subject {
public string SubjectName {
get;
set;
}
public decimal Marks {
get;
set;
}
}
}
خروجی
مثال سوم
تا کنون، استفاده از تابع محلی را برای چاپ برخی مقادیر یا برای بازگشت یک متغیر محاسبه شده توضیح دادم، اما جدا از این، شما می توانید خیلی کارهای بیشتری انجام دهید. در این مثال، من می خواهم توضیح دهم که چگونه می توانیم به جای تابع بازگشتی از تابع محلی استفاده کنیم.
فاکتوریل را با تابع بازگشتی محاسبه کنید.
private static long GetFactorial(int number) {
if (number < 0) throw new ArgumentException("negative number", nameof(number));
if (number == 0) return 1;
return number * GetFactorial(number - 1);
}
فاکتوریل را با کمک تابع محلی محاسبه کنید.
private static long GetFactorialUsingLocal(int number) {
if (number < 0) throw new ArgumentException("negative number", nameof(number));
if (number == 0) return 1;
long result = number;
while (number > 1) {
Multiply(number - 1);
number--;
}
void Multiply(int x) => result *= x;
return result;
}
من می دانم که اکثر شما آگاه هستید که برای محاسبه فاکتوریل، نیازی نیست که تابع بازگشتی را به حساب آوریم. شما ممکن است فکر کنید که فاکتوریل را نیز می توان بدون استفاده از عملکرد بازگشتی محاسبه کرد، پس چرا من در مورد عملکرد بازگشتی بحث می کنم؟
من در مورد تابع بازگشتی بحث می کنم چون می خواهم مقایسه بین عملکرد تابع بازگشتی با تابع محلی را به شما نشان دهم و پیشنهاد کنم که تابع بازگشتی را با تابع محلی جایگزین کنید.
نکته: تابع محلی نیازی به نگه داشتن call stack ندارد. (تصاویر زیر را مشاهده کنید)
- اکثر توسعه دهندگان از Call Stack مانند یک نقشه برای اشکال زدایی برنامه خود استفاده می کنند.
حال که حرف از call stack شد بیایید یک مطلب بیشتر یاد بگیریم:
"اگر call stack شما بیش از حد طولانی باشد، برنامه شما شکست خواهد خورد."
یک مثال واضح تر:
بیایید درخواست مشابه برای محاسبه فاکتوریل 9K را امتحان کنیم.
در تصویر بالا خطای “System.StackOverflowException” می بینید که متوجه می شوید برنامه خراب شده است.
حال تابع محلی را برای محاسبه فاکتوریل 9k صدا میزنیم:
class Program {
static void Main(string[] args) {
//BigInteger factorial = GetFactorial(9000);
BigInteger factorial = GetFactorialUsingLocal(9000);
}
private static BigInteger GetFactorialUsingLocal(int number) {
if (number < 0) throw new ArgumentException("negative number", nameof(number));
else if (number == 0) return 1;
BigInteger result = number;
while (number > 1) {
Multiply(number - 1);
number--;
}
void Multiply(int x) => result *= x;
return result;
}
private static BigInteger GetFactorial(int number) {
if (number < 0) throw new ArgumentException("negative number", nameof(number));
return number == 0 ? 1 : number * GetFactorial(number - 1);
}
}
با این حال شما باز می توانید آزمایشات بیشتری در مورد توابع محلی انجام دهید.
Binary Literal
همانطور که می دانید تا قبل از C# 7.0 فقط از decimal و hexadecimal پشتیبانی می شد.
Decimal Literal
int length = 50;
Hexadecimal Literal
int number = 0X3E8;
یا
int number = 0x3E8;
می توانید ببینید که هنگام استفاده از پیشوند با نوع داده ای صحیح، پیش فرض یک "عدد" در یک سیستم دهدهی نیست، در حالی که برای یک عدد شانزده، ما باید از پیشوند "0X" یا "0x " استفاده کنیم (می توانید x کوچک یا X را استفاده کنید).
اما هنگامی که شما آن را با استفاده از Console.WriteLine() چاپ می کنید، آن را همیشه در مقدار دهدهی چاپ می کند. در زیر یک مثال واضح آمده است:
int Int32MaxLengthInDecimal = 2147483647;
int Int32MaxLengthInHexaDecimal = 0x7FFFFFFF;
WriteLine($ "Int32MaxLengthInDecimal {Int32MaxLengthInDecimal}");
WriteLine($ "Int32MaxLengthInHexaDecimal {Int32MaxLengthInHexaDecimal}");
خروجی
حال می توانید در C # 7 از الفبای دودویی نیز استفاده کنید. شما می توانید "0B" یا "b0" را برای حروف باینری استفاده کنید.
بنابراین، در C # 7، می توانید عدد صحیح را از 3 راه بیان کنید - عبارات Decimal ، Hexadecimal و Binary
Binary Literal
int x = 0B110010;
or
int x = 0b110010;
using static System.Console;
namespace BinaryLiteral {
class Program {
static void Main(string[] args) {
//Represent 50 in decimal, hexadecimal & binary
int a = 50; // decimal representation of 50
int b = 0X32; // hexadecimal representation of 50
int c = 0B110010; //binary representation of 50
//Represent 100 in decimal, hexadecimal & binary
int d = 50 * 2; // decimal represenation of 100
int e = 0x32 * 2; // hexadecimal represenation of 100
int f = 0b110010 * 2; //binary represenation of 100
WriteLine($ "a: {a:0000} b: {b:0000} c: {c:0000}");
WriteLine($ "d: {d:0000} e: {e:0000} f: {f:0000}");
}
}
}
Digit Separators
Digit separator یک ویژگی جدید در C # 7 است. ما می توانیم از یک یا چند (_) برای جداسازهای رقمی استفاده کنیم. گاهی اوقات، زمانی که ما قصد داریم یک عدد بزرگ را نشان دهیم، این ویژگی مورد نیاز است. بنابراین، قطعه کد چیزی شبیه به آن است.
using static System.Console;
namespace DigitSeparators {
class Program {
static void Main(string[] args) {#
region Using Digit Separators
int binaryData = 0B0010 _0111_0001_0000; // binary value of 10000
int hexaDecimalData = 0X2B _67; //HexaDecimal Value of 11,111
int decimalData = 104 _567_789;
int myCustomData = 1 ___________2__________3___4____5_____6;
double realdata = 1 _000 .111 _1e1_00;
WriteLine($ " binaryData :{binaryData} \n hexaDecimalData: {hexaDecimalData} \n decimalData: {decimalData} \n myCustomData: {myCustomData} \n realdata: {realdata}");#
endregion
}
}
}
Pattern matching
Pattern matching از الگوهای مختلفی پشتیبانی می کند. در زیر یک کد ساده برای درک آن آمده است:
using static System.Console;
namespace PatternMatching {
class Program {
static void Main(string[] args) {
//Example 1
var myData = "Custom Data";
var myData2 = myData is string ? "String" : "Not a string";
var myData3 = myData is string a ? a : "Not a String";
WriteLine(myData2);
WriteLine(myData3);
//Example 2
var x = 10;
dynamic y = 0b1001;
var sum = y is int ? $ "{y * x}" : "Invalid data";
WriteLine(sum);
}
}
}
این یک مثال است. در کل می توان از الگوهای Constant Pattern, Var Pattern, Recursive Pattern, Property Pattern & Property Sub-pattern, Switch Statement, Match Expression, Case expression, Throw expression, Destructuring assignment, Testing Nullable, Arithmetic simplification, Tuple decomposition, Complex Pattern, Wildcard Pattern استفاده کرد.
Out Variables (enhancement for out variables)
قبل از C # 7، اگر شما می خواستید یک متغیر را به عنوان پارامتر خروجی فراخوانی کنید، مجبور بودید قبل از دادن آن به متد، متغیر را اعلام کنید. اما در C # 7، شما می توانید متغیر را مستقیماً و زمانی که آن را درون یک متد قرار می دهید، اعلام کنید.
در تصویر بالا، شما می توانید ببینید که من در C # 7 یک متغیر خارجی را در داخل متد فراخوانی می کنم و از خارج نیز از آن استفاده می شود.
using static System.Console;
class Program {
static void Main(string[] args) {
string s = "26-Nov-2016";
if (DateTime.TryParse(s, out DateTime date)) {
WriteLine(date);
}
WriteLine(date);
}
ممکن است فکر کنید که اگر استثناء رخ دهد، چه اتفاقی خواهد؟
اگر هر استثنائی در آن وجود داشته باشد، متغیر خروجی با مقدار پیش فرض تعیین می شود. شما می توانید در تصویر زیر ببینید که تاریخ عبور نادرست به DateTime تبدیل نشده است، بنابراین مقدار پیش فرض به متغیر "date" اختصاص داده شده است.
Tuples
زمانی که ما میخواهیم چندین مقداررا بر گردانیم ار Tuple استفاده میکنیم. یک استفاده مناسب از Tuple برگرداندن چندین مقدار از یک متد می باشد.
در C # 7، ما می توانیم Tuples را به طور مستقیم با روش راحت تری استفاده کنیم. کد زیر:
//returning price & discount
(int, int) GetPrice(int itemId) {
var product = (500, 100);
return product;
}
در کد بالا، من می خواهم ItemId را به عنوان پارامتر انتقال دهم و دو مقدار بازگشتی را برگردانم - قیمت اقلام و تخفیف -
اما اگر شما کد بالا را به طور مستقیم در ویژوال استودیو 2017 RC اجرا کنید، کار نخواهد کرد. برای اینکه آن را به درستی کار کنید، باید کمی اضافه کاری انجام دهید. من قصد دارم این مراحل را به صورت یکجا در اینجا توضیح دهم.
تصویر زیر یک خطا را به شما نشان میدهد که در ابتدای کار با آن روبرو می شوید.
راه حل:
System.ValueTuple را از NuGet Package نصب کنید:
- پروژه خود را انتخاب کنید
- بر روی آن کلیک راست کنید
- از منوی Context، Manage NuGet Packages را انتخاب کنید.
- System.ValueTuple را جستجو و نصب کنید.
بعد از انجام این کار، متوجه خواهید شد که این خطا حذف شده است و برنامه شما با موفقیت ساخته شده است.
حال، شما ممکن است فکر کنید که چگونه این روش را فراخوانی کنید. اینکار بسیار ساده است. کافیست نگاهی به قطعه کد زیر داشته باشید.
using static System.Console;
using static System.Text.Encoding;
namespace TupleExampleByBNarayan {
class Program {
static void Main(string[] args) {
OutputEncoding = UTF8;
Program p = new Program();
var price = p.GetPrice(1);
WriteLine($ "Price: ₹{price.Item1}/- \nDiscount: ₹{price.Item2}/-");
}
//returning price & discount
(int, int) GetPrice(int itemId) {
var product = (500, 100);
return product;
}
}
}
خروجی
Deconstruction (splitting tuples)
تا به الان در ذهن شما باید یک سوال مطرح شود - آیا می توانم نام متغیر "price" را مستقیماً بدون استفاده از نام دیگر مجموعه استفاده کنم؟ پاسخ کاملاً واضح است.
ما می توانیم بگوییم که "deconstruction" یک مفهوم است که به ما اجازه می دهد بدون استفاده از نام متغیر، مستقیماً به نام خود دسترسی داشته باشیم و آن را به طور مستقیم با نام های مربوطه استفاده کنیم.
Expression bodied members
Expression bodied members ویژگی جدیدی نیست اما در C# 7.0 پیشرفت بسیاری کرده است.
Expression bodied members یک مقدار یا عبارت را برمی گرداند.
C# 6.0
- Expression bodied Methods
- Expression bodied Properties
C# 7.0
- Expression bodied constructor
- Expression bodied destructor
- Expression bodied getters
- Expression bodied setters
از طریق Expression bodied Methods می توانیم به (private, public, protected, internal and protected internal) دسترسی پیدا کنیم.
Expression bodied Methods
class User {
public int UserId {
get;
set;
}
public string UserName {
get;
set;
}
public string EmailId {
get;
set;
}
public string ContactNumber {
get;
set;
}
public string GetUserDetails() => $ "{UserId} + {UserName} + {EmailId} + {ContactNumber}";
}
Expression bodied Properties
class User {
public int UserId {
get;
} = 1001;
public string UserName {
get;
} = "BNarayan";
public string EmailId {
get;
} = "bnarayan....@gmail.com";
public string ContactNumber {
get;
} = "99xxxxxxxx";
public string UserDetails => $ "{UserId} {UserName} {EmailId} {ContactNumber}";
}
Expression bodied constructor
class Product {
public int ProductId {
get;
} = 1;
public string ProductName {
get;
}
public decimal Price => 3000;
Product() => ProductName = "Microsoft HoloLens";
}
Expression bodied destructor
class Product {
public int ProductId {
get;
} = 1;
public string ProductName {
get;
}
public decimal Price => 3000;
Product() => ProductName = "Microsoft HoloLens";
~Product() => WriteLine("Expression bodied destructor");
}
Expression bodied getters & setters
class Product {
Dictionary < int, decimal > productPriceList = new Dictionary < int, decimal > ();
public int ProductId {
get;
set;
} = 1;
public string ProductName => "Microsoft HoloLens";
public decimal Price {
get => productPriceList[ProductId];
set => productPriceList[ProductId] = value;
}
}
از کد زیر نیز میتوان استفاده کرد:
class Product {
Dictionary < int, decimal > productPriceList = new Dictionary < int, decimal > ();
public int _productId;
public string ProductName => "Microsoft HoloLens";
public decimal Price {
get => productPriceList[_productId];
set => productPriceList[_productId] = value;
}
}
Ref returns and locals
اکثر توسعه دهندگان باید در مورد کلمه کلیدی C# ref و رفتارهای آن آگاه باشند. همانطور که می دانیم آدرس متغیر را به جای مقدار ارزیابی می کند.
در C # 7، می توانیم از 'ref' برای بازگشت یک متغیر از یک متد استفاده کنیم. یعنی یک متد می تواند متغیر را با منبع بازگرداند. ما همچنین می توانیم یک متغیر محلی را با منبع ذخیره کنیم.
Ref Return
int[] x = {
2,
4,
62,
54,
33,
55,
66,
71,
92
};
public ref int GetFirstOddNumber(int[] numbers) {
for (int i = 0; i < numbers.Length; i++) {
if (numbers[i] % 2 == 1) {
return ref numbers[i];
}
}
throw new Exception("odd number not found");
}
But there are some restrictions and everything cannot be returned as reference like below code will give error: Dictionary < int, decimal > ProductPriceList = new Dictionary < int, decimal > ();
public ref decimal GetProductPriceReference(int productId) {
return ref ProductPriceList[productId];
}
خطا
- CS8156 عبارتی است که نمی توان در این زمینه استفاده کرد، زیرا ممکن است توسط منبع بازگردانده نشود.
Ref locals
int[] x = { 2, 4, 62, 54, 33, 55, 66, 71, 92 };
ref int oddNum = ref GetFirstOddNumber(x);
کد کامل
using static System.Console;
namespace RefReturnsAndRefLocals {
class Program {
public ref int GetFirstOddNumber(int[] numbers) {
for (int i = 0; i < numbers.Length; i++) {
if (numbers[i] % 2 == 1) {
return ref numbers[i]; //returning as reference
}
}
throw new Exception("odd number not found");
}
static void Main(string[] args) {
Program p = new Program();
int[] x = {
2,
4,
62,
54,
33,
55,
66,
71,
92
};
ref int oddNum = ref p.GetFirstOddNumber(x); //storing as reference
WriteLine($ "\t\t\t\t{oddNum}");
oddNum = 35;
for (int i = 0; i < x.Length; i++) {
Write($ "{x[i]}\t");
}
ReadKey();
}
}
}
خروجی
Throw Expressions
از طریق Throw میتوان مستقیماً خطاهای موجود را بیرون کشید.
class Program {
static void Main(string[] args) {
var a = Divide(10, 0);
}
public static double Divide(int x, int y) {
return y != 0 ? x % y : throw new DivideByZeroException();
}
} // This is just a sample script. Paste your real code (javascript or HTML) here.
if ('this_is' == /an_example/) {
of_beautifier();
} else {
var a = b ? (c % d) : e[f];
}
از آن دسته از خطاها که Throw از آن ها پشتیبانی می کند میتوان به “IndexOutOfRangeException”, “NullReferenceException”, “OutOfMemoryException”, “StackOverflowException اشاره کرد.
این 10 مورد از مهمترین ویژگی های C# 7.0 در ویژوال استودیو 2017 بود. امیدواریم که بازخورد کافی از این مقاله را برده باشید.
منبع: c-sharpcorner
"با تشکر، مجموعه ABLY"