اهمیت پشتیبانی از SIMD در دات نت
Top Issues Fixed in Visual Studio 2019 version 16.2.5
- Visual Studio stops respsonding on creating a new SSIS project
- Fixed UI freezes occurring after extended usage of the editor.
Security Advisory Notices
CVE-2019-1232 Diagnostics Hub Standard Collector Service Elevation of Privilege Vulnerability
An elevation of privilege vulnerability exists when the Diagnostics Hub Standard Collector Service improperly impersonates certain file operations. An attacker who successfully exploited this vulnerability could gain elevated privileges. An attacker with unprivileged access to a vulnerable system could exploit this vulnerability. The security update addresses the vulnerability by ensuring the Diagnostics Hub Standard Collector Service properly impersonates file operations.
CVE-2019-1301: Denial of Service Vulnerability in .NET Core
A denial of service vulnerability exists when .NET Core improperly handles web requests. An attacker who successfully exploited this vulnerability could cause a denial of service against a .NET Core web application. The vulnerability can be exploited remotely, without authentication.
The update addresses the vulnerability by correcting how the .NET Core web application handles web requests.
Full-Text Search در MongoDB
MongoDB, one of the leading NoSQL databases, is well known for its fast performance, flexible schema, scalability and great indexing capabilities. At the core of this fast performance lies MongoDB indexes, which support efficient execution of queries by avoiding full-collection scans and hence limiting the number of documents MongoDB searches.
Starting from version 2.4, MongoDB began with an experimental feature supporting Full-Text Search using Text Indexes .قصد دارم مجموعه ای کامل از اصول طراحی شیء گرا، الگوهای طراحی و best practice های مربوطه را ارائه دهم. از این رو ابتدا با اصول طراحی شروع میکنم. سپس در مقالات آتی به الگوهای مختلف خواهم پرداخت و تا جایی که معلومات اجازه دهد مشخص خواهم کرد که هر الگو متمرکز بر رعایت کدام یک از اصول است و اینکه آیا مناسب است از الگوی مورد نظر استفاده کنیم.
این مطالب نیز چکیده ای از آموزشهای Lynda, Pluralsight , SkillFeed و کتاب های Gang of four, Headfirst Design
patterns و ...
میباشد .
اصل اول: Encapsulate what varies
"آنچه را که تغییر میکند مشخص و جدا کن یا به عبارتی آنرا کپسوله کن"
برای آنکه بتوانیم کدی منعطف،
قابل استفاده مجدد و خوانا داشته باشیم، ابتدا باید بخشهای ثابت و متغیر کد را تشخیص
دهیم و کاری کنیم تا بخش ثابت، بدون تکرار در جای جای برنامه استفاده شود و سپس برای بخش
متغیر برنامه ریزی کنیم.
اصل دوم: Program to an interface not implementation
" برنامه نویسی را متمرکز بر واسط کن، نه نحوهی پیاده سازی "
برای این اصل تعابیر مختلفی ارائه شده است:
- تمرکز بر واسطها به معنای تمرکز بر رفتار است که باعث میشود انعطاف برنامه بیشتر شده و با تغییر نحوهی پیاده سازی بتوان همچنان سیستمی پایدار داشت.
- تمرکز بر "چه کاری انجام میشود" باعث میشود بدون نگرانی از "چگونگی انجام آن" بتوانیم معماری سیستم را طراحی کنیم.
- واسطها نقش پروتکل را دارند و باعث پنهان شدن نحوهی پیاده سازی از چشم کلاینت (استفاده کنندهی خدمت) میشوند و آنها را ملزم میکنند تا به ورودی و خروجی تمرکز کنند.
برای رعایت این اصل باید:
- سعی بر تعریف واسط برای اکثر کلاسها کنیم
- اشیا را از نوع واسط تعریف
کنیم، نه کلاسهای پیاده ساز آن
در کد زیر نحوهی تعریف واسط را برای کلاس و تعریف اشیاء از نوع واسط را میبینیم:
public interface IMyInterface { void DoWork(); } public class MyImplementation1 : IMyInterface { public void DoWork() { var implementationName = this.GetType().ToString(); Console.WriteLine("DoWork from " + implementationName); } } public class MyImplementation2 : IMyInterface { public void DoWork() { var implementationName = this.GetType().ToString(); Console.WriteLine("DoWork from " + implementationName); } } public class Context { IMyInterface myInterface; public void Print() { myInterface = new MyImplementation1(); myInterface.DoWork(); myInterface = new MyImplementation2(); myInterface.DoWork(); } }
اصل سوم: Favor composition over inheritance
"ترکیب را بر توارث ترجیح بده"
رابطه "دارد" (has a ) انعطاف بیشتری نسبت به ارث بری یا "از نوع ... هست" ( is a ) دارد. برای مثال فرض کنید کلاس Enemy رفتار Movable را دارد و این رفتار در طول بازی بر حسب موقعیتی تغییر میکند. اگر این رفتار را با توارث مدل کنیم، یعنی Enemy از نوع Movable باشد، آنگاه برای هر نوع رفتار Movable (هر نوع پیاده سازی) باید یک نوع Enemy داشته باشیم و تصور کنید بعضی از این پیاده سازیها در کلاس Player یکسان باشد. آنگاه کد باید دوباره تکرار شود. حال تصور کنید این موقعیت را با ترکیب مدل کنیم. آنگاه کلاس Enemy یک شیء از جنس Movable خواهد داشت و در زمان نیاز میتواند نوع این رفتار را با نمونه گیری از کلاسهای پیاده ساز آن، تغییر دهد. در واقع با اینکار اصل اول را رعایت کرده ایم و بخش ثابت را از بخش متغیر جدا نموده ایم.
در زیر مدلسازی با توارث را میبینیم:
public interface Movable { void Move(); } public class EnemyBase : Movable { // Enemy properties goes here protected int x, y; public virtual void Move() { x += 2; y += 2; } } public class EnemyWithMoveType2 : EnemyBase { // override the move method public override void Move() { x += 4; y += 4; } } public class EnemyWithMoveType3 : EnemyBase { // override the move method public override void Move() { x += 6; y += 6; } } public class PlayerBase : Movable { // Player properties goes here protected int x, y; public virtual void Move() { // same code as EnemyBase move method x += 2; y += 2; } } public class PlayerWithMoveType2 : PlayerBase { // override the move method public override void Move() { // same code as EnemyWithMoveType2 move method x += 4; y += 4; } } public class PlayerWithMoveType3 : PlayerBase { // override the move method public override void Move() { // same code as EnemyWithMoveType3 move method x += 6; y += 6; } }
در ادامه نیز مدلسازی با ترکیب را میبینیم:
public interface IMovable { void Move(ref int x, ref int y); } public class EnemyBase { // Enemy properties goes here protected int x, y; IMovable moveBehavior; public void SetMoveBehavior(IMovable _moveBehavior) { moveBehavior = _moveBehavior; } public void Move() { moveBehavior.Move(ref x,ref y); } } public class PlayerBase { // Player properties goes here protected int x, y; IMovable moveBehavior; public void SetMoveBehavior(IMovable _moveBehavior) { moveBehavior = _moveBehavior; } public void Move() { moveBehavior.Move(ref x, ref y); } } public class MoveBehavior1 { public void Move(ref int x, ref int y) { x += 2; y += 2; } } public class MoveBehavior2 : IMovable { public void Move(ref int x, ref int y) { x += 4; y += 4; } } public class MoveBehavior3 : IMovable { public void Move(ref int x, ref int y) { x += 6; y += 6; } }
همانطور که میبینید، با فراخوانی تابع SetMoveBehavior میتوان رفتار را در زمان اجرا تغییر داد.
در مقالهی بعدی به ادامهی اصول خواهم پرداخت. از شنیدن نظرات و سوالات شما خرسند خواهم شد.
The Problem
What they neglect to say is all that testability and persistence ignorance flies right out the window when you create a new ASP.NET Web Application using the MVC template and "Individual User Accounts" authentication. What you get is a single-layered application, tightly coupled to Entity Framework, that:
-
Ignores the patterns that facilitate testing, including: the repository pattern, unit of work pattern, and dependency injection;
-
Forces you to implement their
IUser
interface in your application’s User entity, thereby coupling it to ASP.NET Identity; -
Eliminates any clear separation between your entities, persistence concerns, and business logic. Persistence ignorance? Forget about it.
Thankfully, due to the extensibility designed into ASP.NET Identity, it is possible to ditch the reference to the Microsoft.AspNet.Identity.EntityFramework
assembly and write a custom implementation that can address these and other architectural issues. Just be forewarned: it is not a trivial undertaking, and you’ll have to put up with some code smell that is baked into the Microsoft.AspNet.Identity.Core
assembly.
تغییر اندازه تصاویر #2
دوست گلم توی پستی که شما ارجاع دادین گفته شده که برای تغییر اندازه تصاویر دو راه میشه در نظر گرفت:
1- در زمان ثبت، تصویر تغییر اندازه داده شود.
2- در زمان نیاز تغییر اندازه داده شود.
که در مورد هر کدوم توضیحاتی داده شده است، توی این پست با استفاده از هندلر در زمان اجرا (و استفاده از تابع تغییر اندازه تصویر) تصویر مورد نظر تغییر اندازه داده شده است.
در مورد هندلر هم لینک داده بودم توی پست.
موفق و موید باشید.
در این حالت هش کردن کلمات عبور ایدهی بهتر است. هشها روشهایی یک طرفه هستند که با داشتن نتیجهی نهایی آنها، نمیتوان به اصل کلمهی عبور مورد استفاده دسترسی پیدا کرد. برای بهبود امنیت هشهای تولیدی، میتوان از مفهومی به نام Salt نیز استفاده نمود. Salt در اصل یک رشتهی تصادفی است که پیش از هش شدن نهایی کلمهی عبور، به آن اضافه شده و سپس حاصل این جمع، هش خواهد شد. اهمیت این مساله در بالا بردن زمان یافتن کلمهی عبور اصلی از روی هش نهایی است (توسط روشهایی مانند brute force یا امتحان کردن بازهی وسیعی از عبارات قابل تصور).
اما واقعیت این است که حتی استفاده از یک Salt نیز نمیتواند امنیت بازیابی کلمات عبور هش شده را تضمین کند. برای مثال نرم افزارهایی موجود هستند که با استفاده از پرداش موازی قادرند بیش از 60 میلیارد هش را در یک ثانیه آزمایش کنند و البته این کارآیی، برای کار با هشهای متداولی مانند MD5 و SHA1 بهینه سازی شدهاست.
روش هش کردن کلمات عبور در ASP.NET Identity
ASP.NET Identity 2.x که در حال حاضر آخرین نگارش تکامل یافتهی روشهای امنیتی توصیه شدهی توسط مایکروسافت، برای برنامههای وب است، از استانداردی به نام RFC 2898 و الگوریتم PKDBF2 برای هش کردن کلمات عبور استفاده میکند. مهمترین مزیت این روش خاص، کندتر شدن الگوریتم آن با بالا رفتن تعداد سعیهای ممکن است؛ برخلاف الگوریتمهایی مانند MD5 یا SHA1 که اساسا برای رسیدن به نتیجه، در کمترین زمان ممکن طراحی شدهاند.
PBKDF2 یا password-based key derivation function جزئی از استاندارد RSA نیز هست (PKCS #5 version 2.0). در این الگوریتم، تعداد بار تکرار، یک Salt و یک کلمهی عبور تصادفی جهت بالا بردن انتروپی (بینظمی) کلمهی عبور اصلی، به آن اضافه میشوند. از تعداد بار تکرار برای تکرار الگوریتم هش کردن اطلاعات، به تعداد باری که مشخص شدهاست، استفاده میگردد. همین تکرار است که سبب کندشدن محاسبهی هش میگردد. عدد معمولی که برای این حالت توصیه شدهاست، 50 هزار است.
این استاندارد در دات نت توسط کلاس Rfc2898DeriveBytes پیاده سازی شدهاست که در ذیل مثالی را در مورد نحوهی استفادهی عمومی از آن، مشاهده میکنید:
using System; using System.Diagnostics; using System.Security.Cryptography; using System.Text; namespace IdentityHash { public static class PBKDF2 { public static byte[] GenerateSalt() { using (var randomNumberGenerator = new RNGCryptoServiceProvider()) { var randomNumber = new byte[32]; randomNumberGenerator.GetBytes(randomNumber); return randomNumber; } } public static byte[] HashPassword(byte[] toBeHashed, byte[] salt, int numberOfRounds) { using (var rfc2898 = new Rfc2898DeriveBytes(toBeHashed, salt, numberOfRounds)) { return rfc2898.GetBytes(32); } } } class Program { static void Main(string[] args) { var passwordToHash = "VeryComplexPassword"; hashPassword(passwordToHash, 50000); Console.ReadLine(); } private static void hashPassword(string passwordToHash, int numberOfRounds) { var sw = new Stopwatch(); sw.Start(); var hashedPassword = PBKDF2.HashPassword( Encoding.UTF8.GetBytes(passwordToHash), PBKDF2.GenerateSalt(), numberOfRounds); sw.Stop(); Console.WriteLine(); Console.WriteLine("Password to hash : {0}", passwordToHash); Console.WriteLine("Hashed Password : {0}", Convert.ToBase64String(hashedPassword)); Console.WriteLine("Iterations <{0}> Elapsed Time : {1}ms", numberOfRounds, sw.ElapsedMilliseconds); } } }
پیش فرضهای پیاده سازی Rfc2898DeriveBytes استفاده از الگوریتم SHA1 با 1000 بار تکرار است؛ چیزی که دقیقا در ASP.NET Identity 2.x بکار رفتهاست.
تفاوتهای الگوریتمهای هش کردن اطلاعات در نگارشهای مختلف ASP.NET Identity
اگر به سورس نگارش سوم ASP.NET Identity مراجعه کنیم، یک چنین کامنتی در ابتدای آن قابل مشاهده است:
/* ======================= * HASHED PASSWORD FORMATS * ======================= * * Version 2: * PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations. * (See also: SDL crypto guidelines v5.1, Part III) * Format: { 0x00, salt, subkey } * * Version 3: * PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations. * Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } * (All UInt32s are stored big-endian.) */
در یک چنین حالتی بانک اطلاعاتی ASP.NET Identity 2.x شما با نگارش بعدی سازگار نخواهد بود و تمام کلمات عبور آن باید مجددا ریست شده و مطابق فرمت جدید هش شوند. بنابراین امکان انتخاب الگوریتم هش کردن را نیز پیش بینی کردهاند.
در نگارش دوم ASP.NET Identity، متد هش کردن یک کلمهی عبور، چنین شکلی را دارد:
public static string HashPassword(string password, int numberOfRounds = 1000) { if (password == null) throw new ArgumentNullException("password"); byte[] saltBytes; byte[] hashedPasswordBytes; using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, numberOfRounds)) { saltBytes = rfc2898DeriveBytes.Salt; hashedPasswordBytes = rfc2898DeriveBytes.GetBytes(32); } var outArray = new byte[49]; Buffer.BlockCopy(saltBytes, 0, outArray, 1, 16); Buffer.BlockCopy(hashedPasswordBytes, 0, outArray, 17, 32); return Convert.ToBase64String(outArray); }
چرا در C# 9.0 تا این اندازه بر روی سادگی ایجاد اشیاء Immutable تمرکز شدهاست؟
به شیءای Immutable گفته میشود که پس از وهله سازی ابتدایی آن، وضعیت آن دیگر قابل تغییر نباشد. همچنین به کلاسی Immutable گفته میشود که تمام وهلههای ساخته شدهی از آن نیز Immutable باشند. نمونهی یک چنین شیءای را از نگارش 1 دات نت در حال استفاده هستیم: رشتهها. رشتهها در دات نت غیرقابل تغییر هستند و هرگونه تغییری بر روی آنها، سبب ایجاد یک رشتهی جدید (یک شیء جدید) میشود. نوع جدید record نیز به همین صورت عمل میکند.
مزایای وجود Immutability:
- اشیاء Immutable یا غیرقابل تغییر، thread-safe هستند که در نتیجه، برنامه نویسی همزمان و موازی را بسیار ساده میکنند؛ چون چندین thread میتوانند با شیءای کار کنند که دسترسی به آن، تنها read-only است.
- اشیاء Immutable از اثرات جانبی، مانند تغییرات آنها در متدهای مختلف در امان هستند. میتوانید آنها را به هر متدی ارسال کنید و مطمئن باشید که پس از پایان کار، این شیء تغییری نکردهاست.
- کار با اشیاء Immutable، امکان بهینه سازی حافظه را میسر میکنند. برای مثال NET runtime.، هش رشتههای تعریف شدهی در برنامه را در پشت صحنه نگهداری میکند تا مطمئن شود که تخصیص حافظهی اضافی، برای رشتههای تکراری صورت نمیگیرد. نمونهی دیگر آن نمایش حرف "a" در یک ادیتور یا نمایشگر است. زمانیکه یک شیء Immutable حاوی اطلاعات حرف "a"، ایجاد شود، به سادگی میتوان این تک وهله را جهت نمایش هزاران حرف "a" مورد استفادهی مجدد قرار داد، بدون اینکه نگران مصرف حافظهی بالای برنامه باشیم.
- کار با اشیاء Immutable به باگهای کمتری ختم میشود؛ چون همواره امکان تغییر حالت درونی یک شیء، توسط قسمتهای مختلف برنامه، میتواند به باگهای ناخواستهای منتهی شوند.
- Hash listها که در جهت بهبود کارآیی برنامهها بسیار مورد استفاده قرار میگیرند، بر اساس کلیدهایی Immutable قابل تشکیل هستند.
روش تعریف نوعهای جدید record
کلاس سادهی زیر را در نظر بگیرید:
public class User { public string Name { set; get; } }
public record User { public string Name { set; get; } }
var user = new User(); user.Name = "User 1";
روش تعریف دومی نیز در اینجا میسر است (به آن positional record هم میگویند):
public record User(string Name);
برای کار با رکورد دومی که تعریف کردیم باید سازندهی این record را مقدار دهی کرد:
var user = new User("User 1"); // Error: Init-only property or indexer 'User.Name' can only be assigned // in an object initializer, or on 'this' or 'base' in an instance constructor // or an 'init' accessor. [CS9Features]csharp(CS8852) user.Name = "User 1";
نوع جدید record چه اطلاعاتی را به صورت خودکار تولید میکند؟
روش دوم تعریف recordها اگر در نظر بگیریم:
public record User(string Name);
using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; using CS9Features; public class User : IEquatable<User> { protected virtual Type EqualityContract { [System.Runtime.CompilerServices.NullableContext(1)] [CompilerGenerated] get { return typeof(User); } } public string Name { get; set/*init*/; } public User(string Name) { this.Name = Name; base..ctor(); } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("User"); stringBuilder.Append(" { "); if (PrintMembers(stringBuilder)) { stringBuilder.Append(" "); } stringBuilder.Append("}"); return stringBuilder.ToString(); } protected virtual bool PrintMembers(StringBuilder builder) { builder.Append("Name"); builder.Append(" = "); builder.Append((object?)Name); return true; } [System.Runtime.CompilerServices.NullableContext(2)] public static bool operator !=(User? r1, User? r2) { return !(r1 == r2); } [System.Runtime.CompilerServices.NullableContext(2)] public static bool operator ==(User? r1, User? r2) { return (object)r1 == r2 || (r1?.Equals(r2) ?? false); } public override int GetHashCode() { return EqualityComparer<Type>.Default.GetHashCode(EqualityContract) * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name); } public override bool Equals(object? obj) { return Equals(obj as User); } public virtual bool Equals(User? other) { return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<string>.Default.Equals(Name, other!.Name); } public virtual User <Clone>$() { return new User(this); } protected User(User original) { Name = original.Name; } public void Deconstruct(out string Name) { Name = this.Name; } }
- recordها هنوز هم در اصل همان classهای استاندارد #C هستند (یعنی در اصل reference type هستند).
- این کلاس به همراه یک سازنده و یک خاصیت init-only است (بر اساس تعاریف ما).
- متد ToString آن بازنویسی شدهاست تا اگر آنرا بر روی شیء حاصل، فراخوانی کردیم، به صورت خودکار نمایش زیبایی را از محتوای آن ارائه دهد.
- این کلاس از نوع <IEquatable<User است که امکان مقایسهی اشیاء record را به سادگی میسر میکند. برای این منظور متدهای GetHashCode و Equals آن به صورت خودکار بازنویسی و تکمیل شدهاند (یعنی مقایسهی آن شبیه به value-type است).
- این کلاس امکان clone کردن اطلاعات جاری را مهیا میکند.
- همچنین به همراه یک متد Deconstruct هم هست که جهت انتساب خواص تعریف شدهی در آن، به یک tuple مفید است.
بنابراین یک رکورد به همراه قابلیتهایی است که سالها در زبان #C وجود داشتهاند و شاید ما به سادگی حاضر به تشکیل و تکمیل آنها نمیشدیم؛ اما اکنون کامپایلر زحمت کدنویسی خودکار آنها را متقبل میشود!
ساخت یک وهلهی جدید از یک record با clone کردن آن
اگر به کدهای حاصل از دیکامپایل فوق دقت کنید، یک قسمت جدید clone هم با syntax خاصی در آن ظاهر شدهاست:
public virtual User <Clone>$() { return new User(this); }
public record User(string Name, int Age);
var user1 = new User("User 1", 21);
var user2 = user1 with { Age = 31 };
مقایسهی نوعهای record
در کدهای حاصل از دیکامپایل فوق، قسمت عمدهای از آن به تکمیل اینترفیس <IEquatable<User پرداخته شده بود. به همین جهت اکنون دو رکورد با مقادیر خواص یکسانی را ایجاد میکنیم:
var user1 = new User("User 1", 21); var user2 = new User("User 1", 21);
Console.WriteLine("user1.Equals(user2) -> {0}", user1.Equals(user2)); Console.WriteLine("user1 == user2 -> {0}", user1 == user2);
user1.Equals(user2) -> True user1 == user2 -> True
- زمانیکه عملگر == را بر روی شیء user1 و user2 اعمال میکنیم، اگر User، از نوع کلاس معمولی باشد، حاصل آن false خواهد بود؛ چون این دو، به یک مکان از حافظه اشاره نمیکنند، حتی با اینکه مقادیر خواص هر دو شیء یکی است.
- اما اگر به قطعه کد دیکامپایل شده دقت کنید، در یک رکورد که هر چند در اصل یک کلاس است، حتی عملگر == نیز بازنویسی شدهاست تا در پشت صحنه همان متد Equals را فراخوانی کند و این متد با توجه به پیاده سازی اینترفیس <IEquatable<User، اینبار دقیقا مقادیر خواص رکورد را یک به یک مقایسه کرده و نتیجهی حاصل را باز میگرداند:
public virtual bool Equals(User? other) { return (object)other != null && EqualityContract == other!.EqualityContract && EqualityComparer<string>.Default.Equals(Name, other!.Name) && EqualityComparer<int>.Default.Equals(Age, other!.Age); }
یک نکته: بازنویسی عملگر == در SDK نگارش rc2 فعلی رخدادهاست و در نگارشهای قبلی preview، اینگونه نبود.
امکان ارثبری در recordها
دو رکورد زیر را در نظر بگیرید که اولی به همراه Name است و نمونهی مشتق شدهی از آن، خاصیت init-only سن را نیز به همراه دارد:
public record User { public string Name { get; init; } public User(string name) { Name = name; } } public record UserWithAge : User { public int Age { get; init; } public UserWithAge(string name, int age) : base(name) { Age = age; } }
var user1 = new User("User 1"); var user2 = new UserWithAge("User 1", 21); Console.WriteLine("user1.Equals(user2) -> {0}", user1.Equals(user2)); Console.WriteLine("user1 == user2 -> {0}", user1 == user2);
user1.Equals(user2) -> False user1 == user2 -> False
امکان تعریف ارثبری رکوردها به صورت زیر نیز وجود دارد و الزاما نیازی به روش تعریف کلاس مانند آنها، مانند مثال فوق نیست:
public abstract record Food(int Calories); public record Milk(int C, double FatPercentage) : Food(C);
رکوردها متد ToString را بازنویسی میکنند
در مثال قبلی اگر یک ToString را بر روی اشیاء تشکیل شده فراخوانی کنیم:
Console.WriteLine(user1.ToString()); Console.WriteLine(user2.ToString());
User { Name = User 1 } UserWithAge { Name = User 1, Age = 21 }
امکان استفادهی از Deconstruct در رکوردها
دو روش برای تعریف رکوردها وجود دارند؛ یکی شبیه به تعریف کلاسها است و دیگری تعریف یک سطری، که positional record نیز نامیده میشود:
public record Person(string Name, int Age);
public void Deconstruct(out string Name, out int Age) { Name = this.Name; Age = this.Age; }
var (name, age) = new Person("User 1", 21);
امکان استفادهی از نوعهای record در ASP.NET Core 5x
سیستم model binding در ASP.NET Core 5x، از نوعهای record نیز پشتیبانی میکند؛ یک مثال:
public record Person([Required] string Name, [Range(0, 150)] int Age); public class PersonController { public IActionResult Index() => View(); [HttpPost] public IActionResult Index(Person person) { // ... } }
پرسش و پاسخ
آیا نوعهای record به صورت value type معرفی میشوند؟
پاسخ: خیر. رکوردها در اصل reference type هستند؛ اما از لحاظ مقایسه، شبیه به value types عمل میکنند.
آیا میتوان در یک کلاس، خاصیتی از نوع رکورد را تعریف کرد؟
پاسخ: بله. از این لحاظ محدودیتی وجود ندارد.
آیا میتوان در رکوردها، از struct و یا کلاسها جهت تعریف خواص استفاده کرد؟
پاسخ: بله. از این لحاظ محدودیتی وجود ندارد.
آیا میتوان از واژهی کلیدی with با کلاسها و یا structها استفاده کرد؟
پاسخ: خیر. این واژهی کلیدی در C# 9.0 مختص به رکوردها است.
آیا رکوردها به صورت پیشفرض Immutable هستند؟
پاسخ: اگر آنها را به صورت positional records تعریف کنید، بله. چون در این حالت خواص تشکیل شدهی توسط آنها از نوع init-only هستند. در غیراینصورت، میتوان خواص غیر init-only را نیز به تعریف رکوردها اضافه کرد.