مقدمه
در درس قبل، اصل کپسولهسازی را با استفاده از فیلدهای خصوصی (private) و پراپرتیهای عمومی (public)
پیادهسازی کردیم. دیدیم که این الگو به ما اجازه میدهد تا با افزودن منطق در اکسسرهای get و set، کنترل کاملی بر روی دادههای
کلاس داشته باشیم. اما در بسیاری از موارد، ما به هیچ منطق سفارشی در پراپرتی نیاز نداریم و صرفاً
میخواهیم مقدار یک فیلد خصوصی را در معرض دید قرار دهیم. در چنین سناریوهایی، نوشتن کامل فیلد
پشتیبان و اکسسورها میتواند به کدی تکراری و طولانی منجر شود. برای حل این مشکل، C# یک
سینتکس بسیار مختصر و تمیز به نام پراپرتیهای خودکار (Auto-Implemented
Properties) ارائه میدهد.
الگوی تکراری پراپرتیهای کامل
بیایید ابتدا به الگویی که در درس قبل برای یک پراپرتی ساده (بدون منطق اعتبارسنجی) استفاده کردیم،
نگاهی بیندازیم.
Program.cs
public class Student
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
در این کد، تمام کاری که پراپرتی Name انجام میدهد این است که به عنوان یک واسط برای فیلد خصوصی
_name عمل کند. هیچ منطق اضافهای در کار نیست. این کد کاملاً صحیح است، اما میتوان آن را بسیار
کوتاهتر نوشت. این نوع کد تکراری که صرفاً برای پیادهسازی یک الگو نوشته میشود، اصطلاحاً
کد boilerplate نامیده میشود.
پراپرتیهای خودکار: راه حل مختصر
پراپرتیهای خودکار به ما این امکان را میدهند که بدون نیاز به
تعریف صریح یک فیلد خصوصی، یک پراپرتی عمومی تعریف کنیم. کامپایلر C# به صورت خودکار یک
فیلد پشتیبان خصوصی و ناشناس در پشت صحنه ایجاد میکند.
سینتکس پراپرتیهای خودکار
سینتکس این نوع پراپرتیها بسیار ساده است:
C#
public string Name { get; set; }
همین! کد بالا دقیقاً معادل کد طولانیای است که در بخش قبل نوشتیم. کامپایلر به طور خودکار فیلد
پشتیبان را ایجاد کرده و اکسسورهای get و هر set را به آن متصل میکند. بیایید کلاس Student را
با استفاده از این سینتکس بازنویسی کنیم.
Program.cs
public class Student
{
public string Name { get; set; }
public int StudentID { get; set; }
}
Student student1 = new Student();
student1.Name = "Alice";
student1.StudentID = 101;
Console.WriteLine($"Student: {student1.Name}, ID: {student1.StudentID}");
از دید کدی که از این کلاس استفاده میکند، هیچ تفاوتی بین یک پراپرتی کامل و یک پراپرتی خودکار
وجود ندارد. هر دو به یک شکل فراخوانی و استفاده میشوند. این ویژگی به ما اجازه میدهد کدهایی
بسیار تمیزتر و خواناتر بنویسیم.
کنترل دسترسی در پراپرتیهای خودکار
ممکن است فکر کنید با استفاده از پراپرتیهای خودکار، قابلیت کنترل دسترسی را از دست میدهیم، اما
اینطور نیست. ما همچنان میتوانیم برای اکسسورهای get و set سطح دسترسی متفاوتی تعریف کنیم.
پراپرتیهای فقط-خواندنی خودکار
یک سناریوی بسیار رایج، ایجاد پراپرتیهایی است که مقدار آنها فقط در زمان ساخت شیء (در سازنده)
تعیین میشود و پس از آن نباید از خارج کلاس تغییر کند. برای این کار، میتوانیم اکسسور set را به
صورت private تعریف کنیم.
Program.cs
public class Transaction
{
public Guid TransactionId { get; private set; }
public decimal Amount { get; set; }
public Transaction(decimal amount)
{
TransactionId = Guid.NewGuid();
Amount = amount;
}
}
Transaction tx = new Transaction(50.0m);
Console.WriteLine($"ID: {tx.TransactionId}, Amount: {tx.Amount}");
در این مثال، TransactionId یک شناسهی یکتا برای تراکنش است که به محض ایجاد، نباید تغییر کند.
با private کردن اکسسور set، ما این تضمین را ایجاد کردهایم.
اکسسرهای init-only
در نسخههای مدرن C# (9.0 و بالاتر)، راهکار بهتری برای ایجاد پراپرتیهای تغییرناپذیر
(immutable) معرفی شده است: اکسسور init. یک پراپرتی که با init تعریف میشود،
فقط میتواند در زمان ساخت شیء (در سازنده یا در یک مقدارده اولیهی شیء) مقدار بگیرد. پس از آن، به
یک پراپرتی فقط-خواندنی تبدیل میشود.
Program.cs
public class ImmutablePoint
{
public int X { get; init; }
public int Y { get; init; }
}
ImmutablePoint p1 = new ImmutablePoint { X = 10, Y = 20 };
اکسسرهای init برای طراحی انواع دادهی تغییرناپذیر که امنیت و پیشبینیپذیری کد را افزایش
میدهند، ابزاری بسیار قدرتمند و مدرن هستند.
چه زمانی از کدام نوع پراپرتی استفاده کنیم؟
- پراپرتی خودکار ({ get; set; }): گزینهی پیشفرض شما. زمانی که به
سادگی میخواهید یک فیلد را در معرض دید قرار دهید، از این نوع استفاده کنید.
- پراپرتی خودکار فقط-خواندنی ({ get; private set; } یا { get; init; }): زمانی که میخواهید یک مقدار پس از ایجاد شیء تغییر نکند (یا فقط از داخل
کلاس تغییر کند)، این گزینهها عالی هستند. init تغییرناپذیری قویتری را تضمین میکند.
- پراپرتی کامل (با فیلد پشتیبان): فقط زمانی از این نوع
استفاده کنید که نیاز به اجرای یک منطق سفارشی در اکسسور get یا set
دارید (مانند اعتبارسنجی دادهها، اجرای یک محاسبه، یا اطلاعرسانی در مورد تغییر مقدار).