اشتراکها
اشتراکها
LINQPad بر روی macOS
LINQPad is coming to macOS! using AvaloniaUI XPF
یک مثال : فرض کنید میخواهیم فراوانی کلمات موجود در یک متن را از طریق عملگر GroupBy محاسبه کنیم :
var article = "date datetime datelocal Groups dnttips the elements Groups the elements dnttips the".Split(' '); var result = article.GroupBy(x => x).Select(x => new { Word = x.Key, Count = x.Count() }); foreach (var item in result) { Console.WriteLine($"{item.Word} : {item.Count}"); }
خروجی
date : 1 datetime : 1 datelocal : 1 Groups : 2 dnttips : 2 the : 3 elements : 2
در مطلب «C# 7 - Tuple return types and deconstruction» با نوعهای جدید بازگشتی Tuple در C# 7.0 آشنا شدیم. در C# 7.1 تشخیص نام اعضای Tuple تعریف شده بهبود یافته و از این لحاظ شبیه به anonymous types شدهاست. مفهوم «Name Inference» یا «حدس زدن نامها» را با یک مثال بهتر میتوان توضیح داد.
در C# 7.0 مفهوم «Name Inference» پیاده سازی نشدهاست. به همین جهت کامپایلر قادر نیست نام اعضای Tuple تعریف شدهی فوق را حدس بزند و برای دسترسی به آنها باید تنها از Item1 و Item2 مانند قبل استفاده کرد. البته اگر برای اعضای Tuple نام تعریف کنیم (قسمت «مفهوم Tuple Literals»)، آنگاه میتوان Item1 و Item2 را با نامهای این اعضاء جایگزین کرد:
بنابراین ذکر نام صریح اعضای Tuple در سیشارپ 7 الزامی است؛ در غیراینصورت باید با همان نامهای عمومی Item1 و Item2 جهت دسترسی به این اعضاء، کار کرد.
این وضعیت در C# 7.1 بهبود یافتهاست و اکنون کامپایلر در صورت عدم ذکر صریح نام اعضای Tuple، قادر است این نامها را دقیقا بر اساس نام متغیرها، همانند قابلیتی که در مورد anonymous types وجود دارد، تعیین کند و حدس بزند:
این مثال، شبیه به اولین مثالی است که در مورد C# 7.0 ذکر شد. اما در C# 7.1 نیازی به ذکر Item1 و Item2 جهت دسترسی به اعضای Tuple تعریف شده نیست (هرچند هنوز هم مجاز است) و کامپایلر نام این اعضاء را از نام متغیرهای متناظر با آنها حدس میزند.
مثالهایی از حدس زدن نامهای اعضای Tuple در C# 7.1
مثال اول همان حدس زدن نامهای اعضای Tuple بر اساس نام متغیرهای محلی متناظر با آنها است.
مثال دوم بر اساس نام خواص یک شیء است که توسط یک نوع Tuple بازگشت داده میشود:
در اینجا عملگر .? نیز پشتیبانی میشود:
مثال سوم همان مثال دوم است که در یک عبارت LINQ بکار رفتهاست:
در اینجا نوع خروجی عبارت LINQ نوشته شده، لیستی از Tupleها است. در Tuple خروجی آن، نام دو عضو اول، از نام خواص متناظر با آنها حدس زده میشود. نام عنصر سوم به صورت صریح مشخص شدهاست.
نکته 1: حدس زدن نامها در مورد مقادیر خروجی متدها رخ نمیدهد.
در مثال ذیل نمیتوان به personTuple.FirstName بر اساس نام متد ذکر شده دسترسی یافت و تنها میتوان از Item1 در مورد آن استفاده کرد؛ اما در مورد متغیر محلی age میتوان:
نکته 2: اگر نام اعضای Tuple یکی باشند، عملیات حدس زدن نامها رخ نمیدهد.
در این مثال چون Tuple تشکیل شده دارای نامهای یکسان Name است، امکان حدس زدن نامها میسر نیست و در اینجا نیز باید از طریق Item1 و ... به اعضای Tuple دسترسی یافت (و یا میتوان به هر عضو Tuple یک نام منحصربفرد را انتساب داد).
string name = "User 1"; int age = 20; var personTuple = (name, age); Console.WriteLine(personTuple.Item1); // User 1 Console.WriteLine(personTuple.Item2); // 20
string name = "User 1"; int age = 20; var personTuple = (name: name, age:age); Console.WriteLine(personTuple.name); // User 1 Console.WriteLine(personTuple.age); // 20
این وضعیت در C# 7.1 بهبود یافتهاست و اکنون کامپایلر در صورت عدم ذکر صریح نام اعضای Tuple، قادر است این نامها را دقیقا بر اساس نام متغیرها، همانند قابلیتی که در مورد anonymous types وجود دارد، تعیین کند و حدس بزند:
string name = "User 1"; int age = 20; var personTuple = (name, age); Console.WriteLine(personTuple.name); // User 1 Console.WriteLine(personTuple.age); // 20
مثالهایی از حدس زدن نامهای اعضای Tuple در C# 7.1
مثال اول همان حدس زدن نامهای اعضای Tuple بر اساس نام متغیرهای محلی متناظر با آنها است.
مثال دوم بر اساس نام خواص یک شیء است که توسط یک نوع Tuple بازگشت داده میشود:
var p = new Person { Name = "User 1", Age = 20 }; var personTuple = (p.Name, p.Age); Console.WriteLine(personTuple.Name); Console.WriteLine(personTuple.Age);
در اینجا عملگر .? نیز پشتیبانی میشود:
Person p = null; var personTuple = (p?.Name, p?.Age); Console.WriteLine(personTuple.Name); // null Console.WriteLine(personTuple.Age); // null
مثال سوم همان مثال دوم است که در یک عبارت LINQ بکار رفتهاست:
var people = new List<Person> { new Person {Name = "User 1", Age = 42}, new Person {Name = "User 2", Age = 18}, new Person {Name = "User 3", Age = 21} }; var tuples = people .Select(person => ( person.Name, person.Age, NameAndAge: $"{person.Name} is {person.Age}" ) ); var name = tuples.First().Name; var age = tuples.First().Age; var nameAndAge = tuples.First().NameAndAge;
نکته 1: حدس زدن نامها در مورد مقادیر خروجی متدها رخ نمیدهد.
در مثال ذیل نمیتوان به personTuple.FirstName بر اساس نام متد ذکر شده دسترسی یافت و تنها میتوان از Item1 در مورد آن استفاده کرد؛ اما در مورد متغیر محلی age میتوان:
int age = 42; var personTuple = (FirstName(), age); Console.WriteLine(personTuple.Item1); Console.WriteLine(personTuple.age);
نکته 2: اگر نام اعضای Tuple یکی باشند، عملیات حدس زدن نامها رخ نمیدهد.
var p1 = new Person { Name = "User 1", Age = 20 }; var p2 = new Person { Name = "User 2", Age = 22 }; var personTuple = (p1.Name, p2.Name); Console.WriteLine(personTuple.Item1); // User 1 Console.WriteLine(personTuple.Item2); // User 2
مطالب
OpenCVSharp #7
معرفی اینترفیس ++C کتابخانهی OpenCVSharp
اینترفیس یا API زبان C کتابخانهی OpenCV مربوط است به نگارشهای 1x این کتابخانه و تمام مثالهایی را که تاکنون ملاحظه کردید، بر مبنای همین اینترفیس تهیه شده بودند. اما از OpenCV سری 2x، این اینترفیس صرفا جهت سازگاری با نگارشهای قبلی، نگهداری میشود و اینترفیس اصلی مورد استفاده، API جدید ++C آن است. به همین جهت کتابخانهی OpenCVSharp نیز در فضای نام OpenCvSharp.CPlusPlus و توسط اسمبلی OpenCvSharp.CPlusPlus.dll، امکان دسترسی به این API جدید را فراهم کردهاست که در ادامه نکات مهم آنرا بررسی خواهیم کرد.
تبدیل مثالهای اینترفیس C به اینترفیس ++C
مثال «تبدیل تصویر به حالت سیاه و سفید» قسمت سوم را درنظر بگیرید. این مثال به کمک اینترفیس C کتابخانهی OpenCV کار میکند. معادل تبدیل شدهی آن به اینترفیس ++C به صورت ذیل است:
نکاتی را که باید در اینجا مدنظر داشت:
- بجای IplImage، از کلاس Mat استفاده شدهاست.
- برای ایجاد Clone یک تصویر نیازی نیست تا پارامترهای خاصی را به Mat دوم (همان dst) انتساب داد و ایجاد یک Mat خالی کفایت میکند.
- اینبار بجای کلاس Cv اینترفیس C، از کلاس Cv2 اینترفیس ++C استفاده شدهاست.
- متد الحاقی ToBitmap نیز که در کلاس OpenCvSharp.Extensions.BitmapConverter قرار دارد، با نمونهی Mat سازگار است و به این ترتیب میتوان خروجی معادل دات نتی Mat را با فرمت Bitmap تهیه کرد.
- بجای CvWindow، در اینجا باید از Window سازگار با Mat، استفاده شود.
- new Mat معادل Cv2.ImRead است. بنابراین اگر مثال ++C ایی را در اینترنت یافتید:
معادل متد imread آن همان new Mat کتابخانهی OpenCVSharp است و یا متد Cv2.ImRead آن.
کار مستقیم با نقاط در OpenCVSharp
متدهای ماتریسی OpenCV، فوق العاده در جهت سریع اجرا شدن و استفادهی از امکانات سخت افزاری و پردازشهای موازی، بهینه سازی شدهاند. اما اگر قصد داشتید این متدهای سریع را با نمونههایی متداول و نه چندان سریع جایگزین کنید، میتوان مستقیما با نقاط تصویر نیز کار کرد. در ادامه قصد داریم کار فیلتر توکار Not را که عملیات معکوس سازی رنگ نقاط را انجام میدهد، شبیه سازی کنیم.
در اینجا نحوهی دسترسی مستقیم به نقاط تصویر بارگذاری شده را توسط اینترفیس C، ملاحظه میکنید:
IplImage امکان دسترسی به نقاط را به صورت یک آرایهی دو بعدی میسر میکند. خروجی آن از نوع CvColor است که در اینجا از هر عنصر آن، 255 واحد کسر خواهد شد تا فیلتر Not شبیه سازی شود. سپس این رنگ جدید، به نقطهای معادل آن در تصویر خروجی انتساب داده میشود.
روش ارائه شدهی در اینجا یکی از روشهای دسترسی به نقاط، توسط اینترفیس C است. سایر روشهای ممکن را در Wiki آن میتوانید مطالعه کنید.
شبیه به همین کار را میتوان به نحو ذیل توسط اینترفیس ++C کتابخانهی OpenCVSharp نیز انجام داد:
ابتدا توسط کلاس Mat، کار بارگذاری و سپس تهیهی یک کپی، از تصویر اصلی انجام میشود. در ادامه برای دسترسی به نقاط تصویر، از متد Get که خروجی آن از نوع Vec3b است، استفاده خواهد شد. این بردار دارای سه جزء است که بیانگر اجزای رنگ نقطهی مدنظر میباشند. در اینجا نیز 255 واحد از هر جزء کسر شده و سپس توسط متد Set، به تصویر خروجی اعمال خواهند شد.
میتوانید سایر روشهای دسترسی به نقاط را توسط اینترفیس ++C، در Wiki این کتابخانه مطالعه نمائید.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
اینترفیس یا API زبان C کتابخانهی OpenCV مربوط است به نگارشهای 1x این کتابخانه و تمام مثالهایی را که تاکنون ملاحظه کردید، بر مبنای همین اینترفیس تهیه شده بودند. اما از OpenCV سری 2x، این اینترفیس صرفا جهت سازگاری با نگارشهای قبلی، نگهداری میشود و اینترفیس اصلی مورد استفاده، API جدید ++C آن است. به همین جهت کتابخانهی OpenCVSharp نیز در فضای نام OpenCvSharp.CPlusPlus و توسط اسمبلی OpenCvSharp.CPlusPlus.dll، امکان دسترسی به این API جدید را فراهم کردهاست که در ادامه نکات مهم آنرا بررسی خواهیم کرد.
تبدیل مثالهای اینترفیس C به اینترفیس ++C
مثال «تبدیل تصویر به حالت سیاه و سفید» قسمت سوم را درنظر بگیرید. این مثال به کمک اینترفیس C کتابخانهی OpenCV کار میکند. معادل تبدیل شدهی آن به اینترفیس ++C به صورت ذیل است:
// Cv2.ImRead using (var src = new Mat(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor)) using (var dst = new Mat()) { Cv2.CvtColor(src, dst, ColorConversion.BgrToGray); // How to export using (var bitmap = dst.ToBitmap()) // => OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dst) { bitmap.Save("gray.png", ImageFormat.Png); } using (new Window("BgrToGray C++: src", image: src)) using (new Window("BgrToGray C++: dst", image: dst)) { Cv2.WaitKey(); } }
- بجای IplImage، از کلاس Mat استفاده شدهاست.
- برای ایجاد Clone یک تصویر نیازی نیست تا پارامترهای خاصی را به Mat دوم (همان dst) انتساب داد و ایجاد یک Mat خالی کفایت میکند.
- اینبار بجای کلاس Cv اینترفیس C، از کلاس Cv2 اینترفیس ++C استفاده شدهاست.
- متد الحاقی ToBitmap نیز که در کلاس OpenCvSharp.Extensions.BitmapConverter قرار دارد، با نمونهی Mat سازگار است و به این ترتیب میتوان خروجی معادل دات نتی Mat را با فرمت Bitmap تهیه کرد.
- بجای CvWindow، در اینجا باید از Window سازگار با Mat، استفاده شود.
- new Mat معادل Cv2.ImRead است. بنابراین اگر مثال ++C ایی را در اینترنت یافتید:
cv::Mat src = cv::imread ("foo.jpg"); cv::Mat dst; cv::cvtColor (src, dst, CV_BGR2GRAY);
کار مستقیم با نقاط در OpenCVSharp
متدهای ماتریسی OpenCV، فوق العاده در جهت سریع اجرا شدن و استفادهی از امکانات سخت افزاری و پردازشهای موازی، بهینه سازی شدهاند. اما اگر قصد داشتید این متدهای سریع را با نمونههایی متداول و نه چندان سریع جایگزین کنید، میتوان مستقیما با نقاط تصویر نیز کار کرد. در ادامه قصد داریم کار فیلتر توکار Not را که عملیات معکوس سازی رنگ نقاط را انجام میدهد، شبیه سازی کنیم.
در اینجا نحوهی دسترسی مستقیم به نقاط تصویر بارگذاری شده را توسط اینترفیس C، ملاحظه میکنید:
using (var src = new IplImage(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor)) using (var dst = new IplImage(src.Size, src.Depth, src.NChannels)) { for (var y = 0; y < src.Height; y++) { for (var x = 0; x < src.Width; x++) { CvColor pixel = src[y, x]; dst[y, x] = new CvColor { B = (byte)(255 - pixel.B), G = (byte)(255 - pixel.G), R = (byte)(255 - pixel.R) }; } } // [C] Accessing Pixel // https://github.com/shimat/opencvsharp/wiki/%5BC%5D-Accessing-Pixel using (new CvWindow("C Interface: Src", image: src)) using (new CvWindow("C Interface: Dst", image: dst)) { Cv.WaitKey(0); } }
روش ارائه شدهی در اینجا یکی از روشهای دسترسی به نقاط، توسط اینترفیس C است. سایر روشهای ممکن را در Wiki آن میتوانید مطالعه کنید.
شبیه به همین کار را میتوان به نحو ذیل توسط اینترفیس ++C کتابخانهی OpenCVSharp نیز انجام داد:
// Cv2.ImRead using (var src = new Mat(@"..\..\Images\Penguin.Png", LoadMode.AnyDepth | LoadMode.AnyColor)) using (var dst = new Mat()) { src.CopyTo(dst); for (var y = 0; y < src.Height; y++) { for (var x = 0; x < src.Width; x++) { var pixel = src.Get<Vec3b>(y, x); var newPixel = new Vec3b { Item0 = (byte)(255 - pixel.Item0), // B Item1 = (byte)(255 - pixel.Item1), // G Item2 = (byte)(255 - pixel.Item2) // R }; dst.Set(y, x, newPixel); } } // [Cpp] Accessing Pixel // https://github.com/shimat/opencvsharp/wiki/%5BCpp%5D-Accessing-Pixel //Cv2.NamedWindow(); //Cv2.ImShow(); using (new Window("C++ Interface: Src", image: src)) using (new Window("C++ Interface: Dst", image: dst)) { Cv2.WaitKey(0); } }
میتوانید سایر روشهای دسترسی به نقاط را توسط اینترفیس ++C، در Wiki این کتابخانه مطالعه نمائید.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
در EF Core دسترسی به جدول join وجود دارد: «شروع به کار با EF Core 1.0 - قسمت 8 - بررسی رابطهی Many-to-Many»
var usersWithoutUnits = ctx.Users.Where(x => !x.UsersJoinUnits.Any(y => y.UserId == x.UserId)).ToList();
اگر یک لیست رشته ایی داشته باشیم به راحتی میتونیم روی پایگاه داده جستجو کنیم:
var eyeColors = new List<string> { "Black", "Green", "Brown" }; var specificUsers = _context.Users.Where(x => eyeColors.Contains(x.EyeColor)).ToList();
حالا اگه لیست ما ترکیبی باشه این سرچ باید به چه صورت انجام بشه:
public class User { public string FullName { get; set; } public string EyeColor { get; set; } } var customUsers = new List<User> { new User{FullName = "Ali Ahmadi", EyeColor = "Brown"}, new User{FullName = "Milad Rezaei", EyeColor = "Green"} }; var specificUsers = _context.Users.Where(x => customUsers.Contains(new User(){EyeColor = x.EyeColor, FullName = x.FullName})).ToList();
روش فوق و روش های زیادی رو سرچ کردم ولی به جواب نرسیدم. آیا میشه از EF استفاده کرد یا راه حل دیگری دارد ؟
برای کار با React، نیاز است با ES6 آشنایی داشته باشید که در این سایت، یک سری کامل بررسی مقدمات آنرا پیشتر مرور کردهایم. علاوه بر توصیهی مطالعهی این سری (اینکار الزامی است)، در این قسمت خلاصهی بسیار سریع و کاربردی آنرا که بیشتر در برنامههای مبتنی بر React مورد استفاده قرار میگیرند، با هم مرور خواهیم کرد. در قسمتهای بعدی، اهمیت ذکر این خلاصه بیشتر مشخص میشود.
برای بررسی ویژگیهای جاوا اسکریپت مدرن، یک پروژهی جدید React را ایجاد میکنیم.
سپس تمام کدهای داخل index.js را نیز حذف میکنیم. اکنون تمام کدهای خالص جاوا اسکریپتی خود را داخل این فایل خواهیم نوشت.
به علاوه چون در این قسمت خروجی UI نخواهیم داشت، تمام خروجی را در کنسول developer tools مرورگر خود میتوانید مشاهده کنید (فشردن دکمهی F12).
var، let و const
در اکثر زبانهای برنامه نویسی، متغیرها در محدودهی دید قطعه کدی که تعریف شدهاند (scope)، قابل دسترسی هستند. برای نمونه محتوای فایل index.js پروژه را به صورت زیر تغییر داده و با فرض اجرای دستور npm start، خروجی آنرا میتوان در کنسول مرورگر مشاهده کرد.
در این مثال متغیر i، مخصوص قطعه کد حلقه، تعریف شدهاست. بنابراین به ظاهر نباید خارج از این حلقه نیز قابل دسترسی باشد. اما خروجی آن به صورت زیر است:
در آخرین پیمایش حلقه، i مساوی 5 شده و از حلقه خارج میشود. اما چون در اینجا برای تعریف متغیر از واژهی کلیدی var استفاده شدهاست، محدودهی دید آن به کل تابعی که در آن تعریف شدهاست، بسط پیدا میکند. به همین جهت در این خروجی، عدد 5 را نیز مشاهده میکند که حاصل دسترسی به i، خارج از حلقهاست.
برای یک دست سازی این رفتار با سایر زبانهای برنامه نویسی، در ES6، واژهی کلیدی جدیدی به نام let تعریف شدهاست که میدان دید متغیر را به قطعه کدی که در آن تعریف شدهاست، محدود میکند. اکنون اگر در حلقهی فوق بجای var از let استفاده شود، یک چنین خطایی در مرورگر ظاهر خواهد شد که عنوان میکند، i استفاده شدهی در خارج از حلقه، تعریف نشدهاست.
علاوه بر let، واژهی کلیدی جدید const نیز به ES6 اضافه شدهاست که از آن برای تعریف ثوابت استفاده میشود. constها نیز همانند let، میدان دید محدود شدهای به قطعه کد تعریف شدهی در آن دارند؛ اما قابلیت انتساب مجدد را ندارند:
اگر یک چنین قطعه کدی را اجرا کنیم، خطای x is const را در مرورگر میتوان مشاهده کرد.
به صورت خلاصه از این پس واژهی کلیدی var را فراموش کنید. همیشه با const جهت تعریف متغیرها شروع کنید. اگر به خطا برخوردید و نیاز به انتساب مجدد وجود داشت، آنرا به let تغییر دهید. بنابراین استفاده از const همیشه نسبت به let ارجحیت دارد.
اشیاء در جاوا اسکریپت
اشیاء در جاوا اسکریپت به صورت مجموعهای از key/valueها تعریف میشوند:
در اینجا امکان تعریف یک تابع نیز وجود دارد که چون درون یک شیء قرار میگیرد، اینبار «متد» نامیده میشود. همچنین در ES6 میتوان این متدها را به صورت معمولی، مانند متد talk نیز تعریف کرد که به آنها concise method میگویند. بنابراین نحوهی تعریف فوق را به نحو زیر نیز میتوان خلاصه کرد:
پس از تعریف این شیء، روش دسترسی به اجزای آن به صورت زیر است:
به دو مورد اول، روش dot notation میگویند که از همان ابتدا دقیقا مشخص است کدامیک از خواص و متدهای شیء تعریف شده، مورد استفاده قرار میگیرند.
مورد آخر همان روش استفاده از key/valueها است که اساس اشیاء جاوا اسکریپتی را تشکیل میدهد. البته از این روش فقط زمانی استفاده کنید که قرار است یکسری کار پویا صورت گیرند (مقدار key به صورت متغیر دریافت شود) و از ابتدا مشخص نیست که کدام خاصیت یا متد قرار است تعریف و استفاده شود:
واژهی کلیدی this در جاوا اسکریپت
از واژهی کلیدی this، در قسمتهای بعدی زیاد استفاده خواهیم کرد. به همین جهت نیاز است تفاوتهای اساسی آنرا با سایر زبانهای برنامه نویسی بررسی کنیم.
همان شیء person را که پیشتر تعریف کردیم درنظر بگیرید. در متد walk آن، مقدار this را لاگ میکنیم:
خروجی این قطعه، به صورت زیر است:
شیء this در جاوا اسکریپت، همانند سایر زبانهای برنامه نویسی مانند سیشارپ و یا جاوا رفتار نمیکند. در سایر زبانهای نامبرده شده، this همواره ارجاعی را به وهلهای از شیء جاری، باز میگرداند؛ دقیقا همانند تصویری که در بالا مشاهده میکنید. در اینجا نیز this جاوا اسکریپتی لاگ شده، ارجاعی را به وهلهی جاری شیء person، بازگشت دادهاست. اما مشکل اینجا است که this در جاوا اسکریپت، همیشه به این صورت رفتار نمیکند!
برای نمونه در ادامه یک ثابت را به نام walk تعریف کرده و آنرا به person.walk مقدار دهی میکنیم:
دقت داشته باشید که در اینجا از () استفاده نشدهاست (متد walk اجرا نشدهاست). یعنی صرفا «ارجاعی» از متد walk شیء person را به ثابت walk نسبت دادهایم. بنابراین اکنون ثابت walk نیز یک function است که حاصل console.log آن به صورت زیر است:
سؤال: اکنون اگر این function را با فراخوانی ()walk اجرا کنیم، چه خروجی را میتوان مشاهده کرد؟
اینبار this لاگ شده، به شیء person اشاره نمیکند و شیء استاندارد window مرورگر را بازگشت دادهاست!
اگر یک function به صورت متدی از یک شیء فراخوانی شود، مقدار this همواره اشارهگری به وهلهای از آن شیء خواهد بود. اما اگر این تابع به صورت متکی به خود و به صورت یک function و نه متد یک شیء، فراخوانی شود، اینبار this، شیء سراسری جاوا اسکریپت یا همان شیء window را بازگشت میدهد.
یک نکته: اگر strict mode جاوا اسکریپت را در پروژهی جاری فعال کنیم، بجای شیء window، مقدار undefined را در خروجی فوق شاهد خواهیم بود.
اتصال مجدد this به شیء اصلی در جاوا اسکریپت
تا اینجا دریافتیم که اگر یک function را به صورت متکی به خود و نه جزئی از یک شیء فراخوانی کنیم، شیء this در این حالت به شیء window سراسری مرورگر اشاره میکند و اگر strict mode فعال باشد، فقط undefined را بازگشت میهد. اکنون میخواهیم بررسی کنیم که چگونه میتوان این مشکل را برطرف کرد؛ یعنی صرفنظر از نحوهی فراخوانی متدها یا تابعها، this همواره ارجاعی را به شیء person بازگشت دهد.
در جاوا اسکریپت، تابعها نیز شیء هستند. برای مثال person.walk نوشته شده نیز یک شیء است. برای اثبات سادهی آن فقط یک دات را پس از person.walk قرار دهید:
همانطور که مشاهده میکنید، شیء person.walk مانند تمام اشیاء دیگر جاوا اسکریپت، به همراه متد bind نیز هست. کار آن، انقیاد یک تابع، به یک شیء است. یعنی هرچیزی را که به عنوان آرگومان آن، به آن ارسال کنیم، به عنوان مقدار شیء this درنظر میگیرد:
در اینجا متد bind، یک وهلهی جدید از person.walk را بازگشت میدهد که در آن شیء person را به عنوان شیء this، تنظیم کردهاست. به همین جهت اینبار فراخوانی walk2 که به شیء person متصل شدهاست، به this صحیحی بجای window سراسری اشاره میکند. از این روش در برنامههای مبتنی بر React زیاد استفاده میشود.
Arrow functions
تابع زیر را درنظر بگیرید که به یک ثابت انتساب داده شدهاست:
در ES6، روش سادهتر و تمیزتری برای این نوع تعاریف، ذیل ویژگی جدید Arrow functions اضافه شدهاست. برای تبدیل قطعه کد فوق به یک arrow function، ابتدا واژهی کلیدی function را حذف میکنیم. سپس بین پارامتر تابع و {}، یک علامت <= (که به آن fat arrow هم میگویند!) قرار میدهیم:
اگر مانند اینجا تنها یک تک پارامتر وجود داشته باشد، میتوان پرانتزهای ذکر شده را نیز حذف کرد:
و اگر این متد پارامتری نداشت، از () استفاده میشود.
در ادامه اگر بدنهی این تابع، فقط حاوی یک return بود، میتوان آنرا به صورت زیر نیز خلاصه کرد (در اینجا {} به همراه واژهی کلیدی return حذف میشوند):
این یک سطر ساده شده، دقیقا معادل اولین const square ای است که نوشتیم. نحوهی فراخوانی آن نیز مانند قبل است.
اکنون مثال مفید دیگری را در مورد Arrow functions بررسی میکنیم که بیشتر شبیه به عبارات LINQ در #C است:
در اینجا آرایهای از اشیاء job را مشاهده میکنید که مورد آخر آن، فعال نیست. اکنون میخواهیم لیست کارهای فعال را گزارشگیری کنیم:
متد filter در جاوا اسکریپت، بر روی تک تک عناصر آرایهی jobs حرکت میکند. سپس هر job را به پارامتر متد ارسالی آن که predicate نام دارد، جهت دریافت یک خروجی true و یا false، ارائه میدهد. اگر خروجی این متد true باشد، آن job انتخاب خواهد شد و در لیست نهایی گزارش، ظاهر میشود.
در ادامه میتوان این تابع را توسط arrow functions به صورت سادهتر زیر نیز نوشت:
ابتدا واژهی کلیدی function را حذف میکنیم. سپس چون یک تک پارامتر را دریافت میکند، نیازی به ذکر پرانتزهای آن نیز نیست. در ادامه چون یک تک return را داریم، { return } را با یک <= جایگزین خواهیم کرد. در اینجا نیازی به ذکر سمیکالن انتهای return هم نیست. نوشتن یک چنین کدی تمیزتر و خواندن آن، سادهتر است.
ارتباط بین arrow functions و شیء this
نکتهی مهمی را که باید در مورد arrow functions دانست این است که شیء this را rebind نمیکنند (rebind: مقدار دهی مجدد؛ ریست کردن).
در مثال زیر، ابتدا شیء user با متد talk که در آن شیء this، لاگ شده، ایجاد شده و سپس این متد فراخوانی گردیدهاست:
همانطور که انتظار میرود، this ای که در اینجا لاگ میشود، دقیقا ارجاعی را به وهلهی جاری شیء user دارد.
اکنون اگر متد لاگ کردن را داخل یک تایمر قرار دهیم چه اتفاقی رخ میدهد؟
متد setTimeout، متدی را که به عنوان آرگومان اول آن دریافت کرده، پس از 1 ثانیه اجرا میکند.
در این حالت خروجی console.log، مجددا همان شیء سراسری window مرورگر است و دیگر به وهلهی جاری شیء user اشاره نمیکند. علت اینجا است که پارامتر اول متد setTimeout که یک callback function نام دارد، جزئی از هیچ شیءای نیست. بنابراین دیگر مانند فراخوانی متد ()user.talk در مثال قبلی کار نمیکند؛ چون متکی به خود است. هر زمان که یک متد متکی به خود غیر وابستهی به یک شیء را اجرا کنیم، به صورت پیشفرض this آن، به شیء window مرورگر اشاره میکند.
سؤال: چگونه میتوان درون یک callback function متکی به خود، به this همان شیء user جاری دسترسی یافت؟
یک روش حل این مساله، ذخیره this شیء user در یک متغیر و سپس ارسال آن به متد متکی به خود setTimeout است:
این روشی است که سالها است وجود دارد؛ با ارائهی arrow functions، دیگر نیازی به اینکار نیست و میتوان از روش زیر استفاده کرد:
در اینجا callback function را تبدیل به یک arrow function کردهایم و چون arrow functions شیء this را rebind نمیکنند، یعنی شیء this را به ارث میبرند. بنابراین console.log مثال فوق، دقیقا به this شیء user اشاره میکند و دوباره آنرا مقدار دهی مجدد نمیکند و از همان نمونهی موجود قطعه کد تعریف شده، استفاده خواهد کرد.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-02.zip
در قسمت بعد نیز بررسی پیشنیازهای جاوا اسکریپتی شروع به کار با React را ادامه خواهیم داد.
برای بررسی ویژگیهای جاوا اسکریپت مدرن، یک پروژهی جدید React را ایجاد میکنیم.
> create-react-app sample-02 > cd sample-02 > npm start
به علاوه چون در این قسمت خروجی UI نخواهیم داشت، تمام خروجی را در کنسول developer tools مرورگر خود میتوانید مشاهده کنید (فشردن دکمهی F12).
var، let و const
در اکثر زبانهای برنامه نویسی، متغیرها در محدودهی دید قطعه کدی که تعریف شدهاند (scope)، قابل دسترسی هستند. برای نمونه محتوای فایل index.js پروژه را به صورت زیر تغییر داده و با فرض اجرای دستور npm start، خروجی آنرا میتوان در کنسول مرورگر مشاهده کرد.
function sayHello() { for (var i = 0; i < 5; i++) { console.log(i); } console.log(i); } sayHello();
در آخرین پیمایش حلقه، i مساوی 5 شده و از حلقه خارج میشود. اما چون در اینجا برای تعریف متغیر از واژهی کلیدی var استفاده شدهاست، محدودهی دید آن به کل تابعی که در آن تعریف شدهاست، بسط پیدا میکند. به همین جهت در این خروجی، عدد 5 را نیز مشاهده میکند که حاصل دسترسی به i، خارج از حلقهاست.
برای یک دست سازی این رفتار با سایر زبانهای برنامه نویسی، در ES6، واژهی کلیدی جدیدی به نام let تعریف شدهاست که میدان دید متغیر را به قطعه کدی که در آن تعریف شدهاست، محدود میکند. اکنون اگر در حلقهی فوق بجای var از let استفاده شود، یک چنین خطایی در مرورگر ظاهر خواهد شد که عنوان میکند، i استفاده شدهی در خارج از حلقه، تعریف نشدهاست.
./src/index.js Line 14:15: 'i' is not defined no-undef Search for the keywords to learn more about each error.
علاوه بر let، واژهی کلیدی جدید const نیز به ES6 اضافه شدهاست که از آن برای تعریف ثوابت استفاده میشود. constها نیز همانند let، میدان دید محدود شدهای به قطعه کد تعریف شدهی در آن دارند؛ اما قابلیت انتساب مجدد را ندارند:
const x = 1; x = 2; // Attempting to override 'x' which is a constant.
به صورت خلاصه از این پس واژهی کلیدی var را فراموش کنید. همیشه با const جهت تعریف متغیرها شروع کنید. اگر به خطا برخوردید و نیاز به انتساب مجدد وجود داشت، آنرا به let تغییر دهید. بنابراین استفاده از const همیشه نسبت به let ارجحیت دارد.
اشیاء در جاوا اسکریپت
اشیاء در جاوا اسکریپت به صورت مجموعهای از key/valueها تعریف میشوند:
const person = { name: "User 1", walk: function() {}, // method talk() {} // concise method };
const person = { name: "User 1", walk() {}, talk() {} };
person.talk(); person.name = "User 3"; person["name"] = "User 2";
مورد آخر همان روش استفاده از key/valueها است که اساس اشیاء جاوا اسکریپتی را تشکیل میدهد. البته از این روش فقط زمانی استفاده کنید که قرار است یکسری کار پویا صورت گیرند (مقدار key به صورت متغیر دریافت شود) و از ابتدا مشخص نیست که کدام خاصیت یا متد قرار است تعریف و استفاده شود:
const targetMember = "name"; person[targetMember] = "User 2";
واژهی کلیدی this در جاوا اسکریپت
از واژهی کلیدی this، در قسمتهای بعدی زیاد استفاده خواهیم کرد. به همین جهت نیاز است تفاوتهای اساسی آنرا با سایر زبانهای برنامه نویسی بررسی کنیم.
همان شیء person را که پیشتر تعریف کردیم درنظر بگیرید. در متد walk آن، مقدار this را لاگ میکنیم:
const person = { name: "User 1", walk() { console.log(this); }, talk() {} }; person.walk();
شیء this در جاوا اسکریپت، همانند سایر زبانهای برنامه نویسی مانند سیشارپ و یا جاوا رفتار نمیکند. در سایر زبانهای نامبرده شده، this همواره ارجاعی را به وهلهای از شیء جاری، باز میگرداند؛ دقیقا همانند تصویری که در بالا مشاهده میکنید. در اینجا نیز this جاوا اسکریپتی لاگ شده، ارجاعی را به وهلهی جاری شیء person، بازگشت دادهاست. اما مشکل اینجا است که this در جاوا اسکریپت، همیشه به این صورت رفتار نمیکند!
برای نمونه در ادامه یک ثابت را به نام walk تعریف کرده و آنرا به person.walk مقدار دهی میکنیم:
const walk = person.walk; console.log(walk);
سؤال: اکنون اگر این function را با فراخوانی ()walk اجرا کنیم، چه خروجی را میتوان مشاهده کرد؟
اینبار this لاگ شده، به شیء person اشاره نمیکند و شیء استاندارد window مرورگر را بازگشت دادهاست!
اگر یک function به صورت متدی از یک شیء فراخوانی شود، مقدار this همواره اشارهگری به وهلهای از آن شیء خواهد بود. اما اگر این تابع به صورت متکی به خود و به صورت یک function و نه متد یک شیء، فراخوانی شود، اینبار this، شیء سراسری جاوا اسکریپت یا همان شیء window را بازگشت میدهد.
یک نکته: اگر strict mode جاوا اسکریپت را در پروژهی جاری فعال کنیم، بجای شیء window، مقدار undefined را در خروجی فوق شاهد خواهیم بود.
اتصال مجدد this به شیء اصلی در جاوا اسکریپت
تا اینجا دریافتیم که اگر یک function را به صورت متکی به خود و نه جزئی از یک شیء فراخوانی کنیم، شیء this در این حالت به شیء window سراسری مرورگر اشاره میکند و اگر strict mode فعال باشد، فقط undefined را بازگشت میهد. اکنون میخواهیم بررسی کنیم که چگونه میتوان این مشکل را برطرف کرد؛ یعنی صرفنظر از نحوهی فراخوانی متدها یا تابعها، this همواره ارجاعی را به شیء person بازگشت دهد.
در جاوا اسکریپت، تابعها نیز شیء هستند. برای مثال person.walk نوشته شده نیز یک شیء است. برای اثبات سادهی آن فقط یک دات را پس از person.walk قرار دهید:
همانطور که مشاهده میکنید، شیء person.walk مانند تمام اشیاء دیگر جاوا اسکریپت، به همراه متد bind نیز هست. کار آن، انقیاد یک تابع، به یک شیء است. یعنی هرچیزی را که به عنوان آرگومان آن، به آن ارسال کنیم، به عنوان مقدار شیء this درنظر میگیرد:
const walk2 = person.walk.bind(person); console.log(walk2); walk2();
Arrow functions
تابع زیر را درنظر بگیرید که به یک ثابت انتساب داده شدهاست:
const square = function(number) { return number * number; };
const square2 = (number) => { return number * number; };
const square2 = number => { return number * number; };
در ادامه اگر بدنهی این تابع، فقط حاوی یک return بود، میتوان آنرا به صورت زیر نیز خلاصه کرد (در اینجا {} به همراه واژهی کلیدی return حذف میشوند):
const square3 = number => number * number; console.log(square3(5));
اکنون مثال مفید دیگری را در مورد Arrow functions بررسی میکنیم که بیشتر شبیه به عبارات LINQ در #C است:
const jobs = [ { id: 1, isActive: true }, { id: 2, isActive: true }, { id: 3, isActive: true }, { id: 4, isActive: true }, { id: 5, isActive: false } ];
var activeJobs = jobs.filter(function(job) { return job.isActive; });
در ادامه میتوان این تابع را توسط arrow functions به صورت سادهتر زیر نیز نوشت:
var activeJobs2 = jobs.filter(job => job.isActive);
ارتباط بین arrow functions و شیء this
نکتهی مهمی را که باید در مورد arrow functions دانست این است که شیء this را rebind نمیکنند (rebind: مقدار دهی مجدد؛ ریست کردن).
در مثال زیر، ابتدا شیء user با متد talk که در آن شیء this، لاگ شده، ایجاد شده و سپس این متد فراخوانی گردیدهاست:
const user = { name: "User 1", talk() { console.log(this); } }; user.talk();
اکنون اگر متد لاگ کردن را داخل یک تایمر قرار دهیم چه اتفاقی رخ میدهد؟
const user = { name: "User 1", talk() { setTimeout(function() { console.log(this); }, 1000); } }; user.talk();
در این حالت خروجی console.log، مجددا همان شیء سراسری window مرورگر است و دیگر به وهلهی جاری شیء user اشاره نمیکند. علت اینجا است که پارامتر اول متد setTimeout که یک callback function نام دارد، جزئی از هیچ شیءای نیست. بنابراین دیگر مانند فراخوانی متد ()user.talk در مثال قبلی کار نمیکند؛ چون متکی به خود است. هر زمان که یک متد متکی به خود غیر وابستهی به یک شیء را اجرا کنیم، به صورت پیشفرض this آن، به شیء window مرورگر اشاره میکند.
سؤال: چگونه میتوان درون یک callback function متکی به خود، به this همان شیء user جاری دسترسی یافت؟
یک روش حل این مساله، ذخیره this شیء user در یک متغیر و سپس ارسال آن به متد متکی به خود setTimeout است:
const user2 = { name: "User 2", talk() { var self = this; setTimeout(function() { console.log(self); }, 1000); } }; user2.talk();
const user3 = { name: "User 3", talk() { setTimeout(() => console.log(this), 1000); } }; user3.talk();
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-02.zip
در قسمت بعد نیز بررسی پیشنیازهای جاوا اسکریپتی شروع به کار با React را ادامه خواهیم داد.
عموما از امکانات LINQ to JSON کتابخانهی JSON.NET زمانی استفاده میشود که ورودی JSON تو در توی حجیمی را دریافت کردهاید اما قصد ندارید به ازای تمام موجودیتهای آن یک کلاس معادل را جهت نگاشت به آنها تهیه کنید و صرفا یک یا چند مقدار تو در توی آن جهت عملیات استخراج نهایی مدنظر است. به علاوه در اینجا LINQ to JSON واژهی کلیدی dynamic را نیز پشتیبانی میکند.
همانطور که در تصویر مشخص است، خروجیهای JSON عموما ترکیبی هستند از مقادیر، آرایهها و اشیاء. هر کدام از اینها در LINQ to JSON به اشیاء JValue، JArray و JObject نگاشت میشوند. البته در حالت JObject هر عضو به یک JProperty و JValue تجزیه خواهد شد.
برای مثال آرایه [1,2] تشکیل شدهاست از یک JArray به همراه دو JValue که مقادیر آنرا تشکیل میدهند. اگر مستقیما بخواهیم یک JArray را تشکیل دهیم میتوان از شیء JArray استفاده کرد:
و اگر یک JSON رشتهای دریافتی را داریم میتوان از متد Parse مربوط به JArray کمک گرفت:
خروجی JArray یک لیست از JTokenها است و با آن میتوان مانند لیستهای معمولی کار کرد.
در حالت کار با اشیاء، شیء JObject امکان تهیه اشیاء JSON ایی را دارا است که میتواند مجموعهای از JPropertyها باشد:
با JObject به صورت dynamic نیز میتوان کار کرد:
این روش بسیار شبیه است به حالتی که با اشیاء جاوا اسکریپتی در سمت کلاینت میتوان کار کرد.
و حالت عکس آن توسط متد JObject.Parse قابل انجام است:
اکنون که با اجزای تشکیل دهندهی LINQ to JSON آشنا شدیم، مثال ذیل را درنظر بگیرید:
خروجی JArray یا JObject از نوع IEnumerable است و بر روی آنها میتوان کلیه متدهای LINQ را فراخوانی کرد. برای مثال در اینجا اولین شیءایی که مقدار خاصیت prop1 آن مساوی value1 است، یافت میشود و یا میتوان اشیاء را بر اساس مقدار خاصیتی مرتب کرده و سپس آنها را بازگشت داد:
امکان انجام sub queries نیز در اینجا پیش بینی شدهاست:
در این مثال، خواص prop2 از نوع آرایهای از اعداد صحیح هستند. با کوئری نوشته شده، اشیایی که خاصیت prop2 آنها دارای عضو 3 است، یافت میشوند.
همانطور که در تصویر مشخص است، خروجیهای JSON عموما ترکیبی هستند از مقادیر، آرایهها و اشیاء. هر کدام از اینها در LINQ to JSON به اشیاء JValue، JArray و JObject نگاشت میشوند. البته در حالت JObject هر عضو به یک JProperty و JValue تجزیه خواهد شد.
برای مثال آرایه [1,2] تشکیل شدهاست از یک JArray به همراه دو JValue که مقادیر آنرا تشکیل میدهند. اگر مستقیما بخواهیم یک JArray را تشکیل دهیم میتوان از شیء JArray استفاده کرد:
var array = new JArray(1, 2, 3); var arrayToJson = array.ToString();
var json = "[1,2,3]"; var jArray= JArray.Parse(json); var val = (int)jArray[0];
در حالت کار با اشیاء، شیء JObject امکان تهیه اشیاء JSON ایی را دارا است که میتواند مجموعهای از JPropertyها باشد:
var jObject = new JObject( new JProperty("prop1", "value1"), new JProperty("prop2", "value2") ); var jObjectToJson = jObject.ToString();
dynamic jObj = new JObject(); jObj.Prop1 = "value1"; jObj.Prop2 = "value2"; jObj.Roles = new[] {"Admin", "User"};
و حالت عکس آن توسط متد JObject.Parse قابل انجام است:
var json = "{ 'prop1': 'value1', 'prop2': 'value2'}"; var jObj = JObject.Parse(json); var val1 = (string)jObj["prop1"];
اکنون که با اجزای تشکیل دهندهی LINQ to JSON آشنا شدیم، مثال ذیل را درنظر بگیرید:
var array = @"[ { 'prop1': 'value1', 'prop2': 'value2' }, { 'prop1': 'test1', 'prop2': 'test2' } ]"; var objects = JArray.Parse(array); var obj1 = objects.FirstOrDefault(token => (string) token["prop1"] == "value1");
var values = objects.OrderBy(token => (string) token["prop1"]) .Select(token => new {Value = (string) token["prop2"]}) .ToList();
var array = @"[ { 'prop1': 'value1', 'prop2': [1,2] }, { 'prop1': 'test1', 'prop2': [1,2,3] } ]"; var objects = JArray.Parse(array); var objectContaining3 = objects.Where(token => token["prop2"].Any(v => (int)v == 3)).ToList();
پیشنهادها
بررسی GraphDiff
به روز رسانی گرافی از اشیاء (مثلا یک رابطه one-to-many یا many-to-many) منقطع از Context در EF کار زمانبری است؛ زیرا EF نمیداند که چه اشیایی در این بین تغییر کردهاند، کدامها حذف شدهاند و کدامها جدید هستند. کتابخانهی GraphDiff راهحلی را برای این مشکل ارائه داده است:
Introducing GraphDiff for Entity Framework Code First
GraphDiff Aggregate Mappings
GraphDiff v1.3.5
Updates to GraphDiff with new scenarios supported
How to update an expression tree with GraphDiff
Update Many-to-Many Association with GraphDiff
Introducing GraphDiff for Entity Framework Code First
GraphDiff Aggregate Mappings
GraphDiff v1.3.5
Updates to GraphDiff with new scenarios supported
How to update an expression tree with GraphDiff
Update Many-to-Many Association with GraphDiff