اشتراکها
Entity Framework در نگارش 7 خود از منابع دادهایی جدیدی پشتیبانی میکند(+) . یعنی از Windows Phone، Windows Store و همچنین ASP.NET 5 (اپلیکیشنهایی که از NET Core. استفاده میکنند) پشتیبانی خواهد کرد. در این نسخه از دیتابیسهای non-relational نیز پشتیبانی میشود. پروایدر SQLite به صورت رسمی توسط تیم EF ارائه شده است که در ادامه نحوهی استفاده از آن را در یک برنامه کنسول ساده بررسی خواهیم کرد.
کار را با بازنویسی متد OnConfiguration شروع میکنیم، در این قسمت باید به EF بگوئیم که میخواهیم از SQLite استفاده کنیم برای اینکار از یک Extension Method با نام UseSQLite و پاس دادن کانکشتن استرینگ به آن استفاده میکنیم.
سپس دستورات زیر را اجرا میکنیم:
توسط دستور Apply-Migrate دیتابیس برای شما ایجاد خواهد شد. البته این دستور زمانی استفاده میشود که برنامه شما یک اپلیکیشن دسکتاپ باشد. اگر اپلیکیشن شما یک Windows Phone Application است باید در زمان اجرای برنامه این کد را بنویسید:
در نهایت میتوانید با دستور زیر از کانتکست برنامه استفاده کرده و خروجی را مشاهده کنید:
کلاسهای برنامه:
using Microsoft.Data.Entity; using Microsoft.Data.Entity.Metadata; using System.Collections.Generic; using System.Linq; namespace UsingEF7WithSQLite { public class Blog { public int BlogId { get; set; } public string Url { get; set; } public List<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } } }
خب تا اینجا مدلهای برنامه را تعریف کردیم، قدم بعدی افزودن پکیج مربوط به پروایدر SQLite به پروژه است، با دستور زیر این پکیج را نصب میکنیم:
PM> Install-Package EntityFramework.SQLite –Pre
اکنون کلاس کانتکست برنامه را به صورت زیر تعریف میکنیم:
namespace UsingEF7SQLiteProvider { public class BloggingContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnConfiguring(DbContextOptions builder) { builder.UseSQLite(@"Data Source=.\BloggingDatabae.db"); } protected override void OnModelCreating(ModelBuilder builder) { builder.Entity<Blog>() .OneToMany(b => b.Posts, p => p.Blog) .ForeignKey(p => p.BlogId); // The EF7 SQLite provider currently doesn't support generated values // so setting the keys to be generated from developer code builder.Entity<Blog>() .Property(b => b.BlogId) .GenerateValueOnAdd(false); builder.Entity<Post>() .Property(b => b.BlogId) .GenerateValueOnAdd(false); } } }
نکته: پروایدر فعلی SQLite در حال حاضر از Generated values پشتیبانی نمیکند، برای این منظور باید درون متد OnModelCreating این قابلیت را غیرفعال کنیم.
اکنون میتوانیم از طریق پاورشل نیوگت دیتابیس را ایجاد کنیم، برای اینکار باید پکیج زیر را به پروژه اضافه کنید:
Install-Package EntityFramework.Commands -Pre
Add-Migration MyFirstMigration Apply-Migration
using (var db = new BloggingContext()) { db.Database.AsMigrationsEnabled().ApplyMigrations(); }
using (var db = new Models.BloggingContext()) { db.Blogs.Add(new Models.Blog { Url = "https://www.dntips.ir" }); db.SaveChanges(); foreach (var item in db.Blogs) { Console.WriteLine(item.Url); } } Console.ReadLine();
اشتراکها
استفاده از MongoDB با Web API
MongoDB is a popular NoSQL database that makes it a great backend for Web APIs which lend themselves towards a document store rather than a relational store. In this blog we show how you can use MongoDB with ASP.NET Web API to build an ApiController with support for HTTP GET, PUT, POST, and DELETE.
اشتراکها
کتابخانه typeorm
در ادامه مطالب منتشر شده در رابطه با قابلیتهای جدید سیشارپ 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";
برای حالت فوق:
برای پراپرتیها:
The previews of EF Core 5.0 require .NET Standard 2.1. This means:
- EF Core 5.0 runs on .NET Core 3.1; it does not require .NET 5.
- This may change in future previews depending on how the plan for .NET 5 evolves.
- EF Core 5.0 runs on other platforms that support .NET Standard 2.1.
- EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.
Version | Original Release Date | Latest Patch Version | Patch Release Date | Support Level | End of Support |
---|---|---|---|---|---|
.NET Core 3.1 | Scheduled for November 2019 | | | Will be LTS when released | |
.NET Core 3.0 | Scheduled for September 23, 2019 | | | Will be Current when released | |
.NET Core 2.2 | December 4, 2018 | 2.2.5 | May 14, 2019 | Current | December 23, 2019 |
.NET Core 2.1 | May 30, 2018 | 2.1.11 | May 14, 2019 | LTS | At least three years from LTS declaration (August 21, 2018) |
.NET Core 2.0 | August 14, 2017 | 2.0.9 | July 10, 2018 | EOL | October 1, 2018 |
.NET Core 1.1 | November 16, 2016 | 1.1.13 | May 14, 2019 | Maintenance | June 27 2019 |
.NET Core 1.0 | June 27, 2016 | 1.0.16 | May 14, 2019 | Maintenance | June 27 2019 |
تا پیش از C# 6 یکی از روشهای توصیه شدهی جهت اتصال رشتهها به هم، استفاده از متدهایی مانند string.Format و StringBuilder.AppendFormat بود:
مشکل این روش، کاهش خوانایی آن با بالا رفتن تعداد پارامترهای متد Format است و همچنین گاهی از اوقات فراموش کردن مقدار دهی بعضی از آنها و یا حتی ذکر ایندکسهایی غیر معتبر که در زمان اجرا، برنامه را با یک خطا متوقف میکنند.
در C# 6 جهت رفع این مشکلات، راه حلی به نام String interpolation ارائه شدهاست و اگر افزونهی ReSharper یا یکی از افزونههای Roslyn را نصب کرده باشید، به سادگی امکان تبدیل کدهای قدیمی را به فرمت جدید آن خواهید یافت:
در این حالت کد قدیمی فوق، به کد ذیل تبدیل خواهد شد:
در اینجا ابتدا یک $ در ابتدای رشته قرار گرفته و سپس هر متغیر به داخل {} انتقال یافتهاست. همچنین دیگر نیازی هم به ذکر string.Format نیست.
عملیاتی که در اینجا توسط کامپایلر صورت خواهد گرفت، تبدیل این کدهای جدید مبتنی بر String interpolation به همان string.Format قدیمی در پشت صحنهاست. بنابراین این قابلیت جدید C# 6 را به کدهای قدیمی خود نیز میتوانید اعمال کنید. فقط کافی است VS 2015 را نصب کرده باشید و دیگر شمارهی دات نت فریم ورک مورد استفاده مهم نیست.
امکان انجام محاسبات با String interpolation
زمانیکه $ در ابتدای رشته قرار گرفت، عبارات داخل {}ها توسط کامپایلر محاسبه و جایگزین میشوند. بنابراین میتوان چنین محاسباتی را نیز انجام داد:
بدیهی اگر $ ابتدای رشته فراموش شود، اتفاق خاصی رخ نخواهد داد.
تغییر فرمت عبارات نمایش داده شده توسط String interpolation
همانطور که با string.Format میتوان نمایش سه رقم جدا کنندهی هزارها را فعال کرد و یا تاریخی را به نحوی خاص نمایش داد، در اینجا نیز همان قابلیتها برقرار هستند و باید پس از ذکر یک : عنوان شوند:
حالت کلی و استاندارد آن در متد string.Format به صورت {index[,alignment][:formatString]} است.
سفارشی سازی String interpolation
اگر متغیر رشتهای معرفی شدهی توسط $ را با یک var مشخص کنیم، نوع آن به صورت پیش فرض، از نوع string خواهد بود. برای نمونه در مثالهای فوق، message و message2 از نوع string تعریف میشوند. اما این رشتههای ویژه را میتوان از نوع IFormattable و یا FormattableString نیز تعریف کرد.
در حقیقت رشتههای آغاز شدهی با $ از نوع IFormattable هستند و اگر نوع متغیر آنها ذکر نشود، به صورت خودکار به نوع FormattableString که اینترفیس IFormattable را پیاده سازی میکند، تبدیل میشوند. بنابراین پیاده سازی این اینترفیس، امکان سفارشی سازی خروجی string interpolation را میسر میکند. برای نمونه میخواهیم در مثال message2، نحوهی نمایش تاریخ را سفارشی سازی کنیم.
در اینجا ابتدا کار با پیاده سازی اینترفیس IFormatProvider شروع میشود. متد GetFormat آن همیشه به همین شکل خواهد بود و هر زمانیکه نوع ارسالی به آن ICustomFormatter بود، یعنی یکی از اجزای {} دار در حال آنالیز است و خروجی مدنظر آن همیشه از نوع ICustomFormatter است که نمونهای از پیاده سازی آنرا جهت سفارشی سازی DateTime ملاحظه میکنید.
پس از پیاده سازی این سفارشی کنندهی تاریخ، نحوهی استفادهی از آن به صورت ذیل است:
ابتدا یک متد static را تعریف کنید که ورودی آن از نوع FormattableString باشد؛ از این جهت که رشتههای شروع شدهی با $ نیز از همین نوع هستند. سپس سفارشی سازی پردازش {}ها در قسمت ToString آن انجام میشود و در اینجا میتوان یک IFormatProvider جدید را معرفی کرد.
در ادامه برای اعمال این سفارشی سازی، فقط کافی است متد formatMyDate را به رشتهی مدنظر اعمال کنیم:
و اگر تنها میخواهید فرهنگ جاری را عوض کنید، از روش سادهی زیر استفاده نمائید:
در اینجا با اعمال متد faIr به عبارت شروع شدهی با $، فرهنگ ایران به رشتهی جاری اعمال خواهد شد.
نمونهی کاربردیتر آن اعمال InvariantCulture به String interpolation است:
یک نکته: همانطور که عنوان شد این قابلیت جدید با نگارشهای قبلی دات نت نیز سازگار است؛ اما این کلاسهای جدید را در این نگارشها نخواهید یافت. برای رفع این مشکل تنها کافی است این کلاسهای یاد شده را به صورت دستی در فضای نام اصلی آنها تعریف و اضافه کنید. یک مثال
غیرفعال سازی String interpolation
اگر میخواهید در رشتهای که با $ شروع شده، بجای محاسبهی عبارتی، دقیقا خود آنرا نمایش دهید (و { را escape کنید)، از {{}} استفاده کنید:
در این مثال اولین {} محاسبه خواهد شد و دومی خیر.
پردازش عبارات شرطی توسط String interpolation
همانطور که عنوان شد، امکان ذکر یک عبارت کامل هم در بین {} وجود دارد (محاسبات، ذکر یک عبارت LINQ، ذکر یک متد و امثال آن). اما در این میان اگر یک عبارت شرطی مدنظر بود، باید بین () قرار گیرد:
علت اینجا است که کامپایلر سیشارپ، : بین {} را به format specifier تفسیر میکند. نمونهی آنرا پیشتر با مثال «تغییر فرمت عبارات نمایش داده شده» ملاحظه کردید. ذکر : در اینجا به معنای شروع مشخص سازی فرمتی است که قرار است به این حاصل اعمال شود. برای تغییر این رفتار پیش فرض، کافی است عبارت مدنظر را بین () ذکر کنیم تا تمام آن به صورت یک عبارت سیشارپ تفسیر شود.
using System; namespace CS6NewFeatures { class Person { public string FirstName { set; get; } public string LastName { set; get; } public int Age { set; get; } } class Program { static void Main(string[] args) { var person = new Person { FirstName = "User 1", LastName = "Last Name 1", Age = 50 }; var message = string.Format("Hello! My name is {0} {1} and I am {2} years old.", person.FirstName, person.LastName, person.Age); Console.Write(message); } } }
در C# 6 جهت رفع این مشکلات، راه حلی به نام String interpolation ارائه شدهاست و اگر افزونهی ReSharper یا یکی از افزونههای Roslyn را نصب کرده باشید، به سادگی امکان تبدیل کدهای قدیمی را به فرمت جدید آن خواهید یافت:
در این حالت کد قدیمی فوق، به کد ذیل تبدیل خواهد شد:
static void Main(string[] args) { var person = new Person { FirstName = "User 1", LastName = "Last Name 1", Age = 50 }; var message = $"Hello! My name is {person.FirstName} {person.LastName} and I am {person.Age} years old."; Console.Write(message); }
عملیاتی که در اینجا توسط کامپایلر صورت خواهد گرفت، تبدیل این کدهای جدید مبتنی بر String interpolation به همان string.Format قدیمی در پشت صحنهاست. بنابراین این قابلیت جدید C# 6 را به کدهای قدیمی خود نیز میتوانید اعمال کنید. فقط کافی است VS 2015 را نصب کرده باشید و دیگر شمارهی دات نت فریم ورک مورد استفاده مهم نیست.
امکان انجام محاسبات با String interpolation
زمانیکه $ در ابتدای رشته قرار گرفت، عبارات داخل {}ها توسط کامپایلر محاسبه و جایگزین میشوند. بنابراین میتوان چنین محاسباتی را نیز انجام داد:
var message2 = $"{Environment.NewLine}Test {DateTime.Now}, {3*2}"; Console.Write(message2);
تغییر فرمت عبارات نمایش داده شده توسط String interpolation
همانطور که با string.Format میتوان نمایش سه رقم جدا کنندهی هزارها را فعال کرد و یا تاریخی را به نحوی خاص نمایش داد، در اینجا نیز همان قابلیتها برقرار هستند و باید پس از ذکر یک : عنوان شوند:
var message3 = $"{Environment.NewLine}{1000000:n0} {DateTime.Now:dd-MM-yyyy}"; Console.Write(message3);
سفارشی سازی String interpolation
اگر متغیر رشتهای معرفی شدهی توسط $ را با یک var مشخص کنیم، نوع آن به صورت پیش فرض، از نوع string خواهد بود. برای نمونه در مثالهای فوق، message و message2 از نوع string تعریف میشوند. اما این رشتههای ویژه را میتوان از نوع IFormattable و یا FormattableString نیز تعریف کرد.
در حقیقت رشتههای آغاز شدهی با $ از نوع IFormattable هستند و اگر نوع متغیر آنها ذکر نشود، به صورت خودکار به نوع FormattableString که اینترفیس IFormattable را پیاده سازی میکند، تبدیل میشوند. بنابراین پیاده سازی این اینترفیس، امکان سفارشی سازی خروجی string interpolation را میسر میکند. برای نمونه میخواهیم در مثال message2، نحوهی نمایش تاریخ را سفارشی سازی کنیم.
class MyDateFormatProvider : IFormatProvider { readonly MyDateFormatter _formatter = new MyDateFormatter(); public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter) ? _formatter : null; } class MyDateFormatter : ICustomFormatter { public string Format(string format, object arg, IFormatProvider formatProvider) { if (arg is DateTime) return ((DateTime)arg).ToString("MM/dd/yyyy"); return arg.ToString(); } } }
پس از پیاده سازی این سفارشی کنندهی تاریخ، نحوهی استفادهی از آن به صورت ذیل است:
static string formatMyDate(FormattableString formattable) { return formattable.ToString(new MyDateFormatProvider()); }
در ادامه برای اعمال این سفارشی سازی، فقط کافی است متد formatMyDate را به رشتهی مدنظر اعمال کنیم:
var message2 = formatMyDate($"{Environment.NewLine}Test {DateTime.Now}, {3*2}"); Console.Write(message2);
و اگر تنها میخواهید فرهنگ جاری را عوض کنید، از روش سادهی زیر استفاده نمائید:
public static string faIr(IFormattable formattable) { return formattable.ToString(null, new CultureInfo("fa-Ir")); }
نمونهی کاربردیتر آن اعمال InvariantCulture به String interpolation است:
static string invariant(FormattableString formattable) { return formattable.ToString(CultureInfo.InvariantCulture); }
یک نکته: همانطور که عنوان شد این قابلیت جدید با نگارشهای قبلی دات نت نیز سازگار است؛ اما این کلاسهای جدید را در این نگارشها نخواهید یافت. برای رفع این مشکل تنها کافی است این کلاسهای یاد شده را به صورت دستی در فضای نام اصلی آنها تعریف و اضافه کنید. یک مثال
غیرفعال سازی String interpolation
اگر میخواهید در رشتهای که با $ شروع شده، بجای محاسبهی عبارتی، دقیقا خود آنرا نمایش دهید (و { را escape کنید)، از {{}} استفاده کنید:
var message0 = $"Hello! My name is {person.FirstName} {{person.FirstName}}";
پردازش عبارات شرطی توسط String interpolation
همانطور که عنوان شد، امکان ذکر یک عبارت کامل هم در بین {} وجود دارد (محاسبات، ذکر یک عبارت LINQ، ذکر یک متد و امثال آن). اما در این میان اگر یک عبارت شرطی مدنظر بود، باید بین () قرار گیرد:
Console.Write($"{(person.Age>50 ? "old": "young")}");
یکی دیگر از قابلیتهای جذاب نسخهی جدید سیشارپ، عملگر nameof است. هدف اصلی آن ارائه کدهایی با قابلیت Refactoring بهتر است؛ زیرا به جای نوشتن نام فیلدها و یا متدها در صورت نیاز به صورت hard-coded، میتوانیم از این عملگر استفاده کنیم. به عنوان مثال در زمان صدور استثناءیی از نوع ArgumentNullException باید نام آرگومان را به سازندهی این کلاس پاس دهیم. متاسفانه یکی از مشکلاتی که با رشتهها در حالت کلی وجود دارد این است که امکان دیباگ در زمان کامپایل را از دست خواهیم داد و با تغییر هر المنت، تغییرات به صورت خودکار به رشته پاس داده شده، به سازندهی کلاس ArgumentNullException اعمال نخواهد شد:
اگر ReSharper را نصب کرده باشید، به شما پیشنهاد میدهد که از nameof به جای یک رشتهی جادویی (magic string) استفاده نمائید:
اما با کمک عملگر nameof میتوانیم قسمت فراخوانی متد OnPropertyChanged را به اینصورت نیز بازنویسی کنیم:
برای آزمایش عملگر nameof میتوانیم یک تست را در حالتهای زیر بنویسیم:
public static void DoWork(string name) { if (name == null) { throw new ArgumentNullException("name"); } }
اما با استفاده از عملگر nameof، کد امنتری را خواهیم داشت؛ زیرا همیشه نام واقعی آرگومان به سازندهی کلاس ArgumentNullException پاس داده میشود:
public static void DoWork(string name) { if (name == null) { throw new ArgumentNullException(nameof(name)); } }
یک مثال دیگر میتواند در زمان فراخوانی رخدادهای مربوط به OnPropertyChanged باشد. در اینجا باید نام خصوصیتی را که تغییر یافته است، به آن پاس دهیم:
public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } }
OnPropertyChanged(nameof(Name));
ممکن است عنوان کنید قبلاً در سیشارپ 5 هم میتوانستیم از ویژگی [CallerMemberName] استفاده کنیم، پس دیگر نیازی به استفاده از عملگر nameof نخواهد بود. اما تفاوت کلیدی این است که CallerMemberName در زمان اجرا نام فیلد فراخوان را دریافت میکند (run time)، در حالیکه با استفاده از عملگر nameof میتوانید در زمان کامپایل به نام فیلد دسترسی داشته باشید (compile time).
محدودیتهای عملگر nameof
این عملگر حالتهایی را که مشاهده میکنید، فعلاً پشتیبانی نخواهد کرد:
nameof(f()); // where f is a method - you could use nameof(f) instead nameof(c._Age); // where c is a different class and _Age is private. Nameof can't break accessor rules. nameof(List<>); // List<> isn't valid C# anyway, so this won't work nameof(default(List<int>)); // default returns an instance, not a member nameof(int); // int is a keyword, not a member- you could do nameof(Int32) nameof(x[2]); // returns an instance using an indexer, so not a member nameof("hello"); // a string isn't a member nameof(1 + 2); // an int isn't a member
همانطور که مشاهده میکنید، همهی حالتهای فوق با موفقیت پاس شدهاند.