وراثت ضمنی از System.Object
وقتی شما یک کلاس را بدون مشخص کردن کلاس پایه تعریف میکنید، کامپایلر C# به طور خودکار
آن را به عنوان یک کلاس مشتقشده از System.Object در نظر میگیرد.
public class Person { }
public class Person : System.Object { }
این وراثت ضمنی به این معناست که هر متغیر از هر نوعی میتواند به یک متغیر از نوع object اختصاص
داده شود. این قابلیت، پایهی اصلی چندریختی در بالاترین سطح ممکن است.
Program.cs
object a = 10;
object b = "Hello";
object c = new Person();
این یکپارچگی به ما اجازه میدهد تا متدها یا کالکشنهایی بسازیم که بتوانند با هر نوع دادهای در
.NET کار کنند. اما مهمتر از آن، این وراثت تضمین میکند که هر شیء در C#
دارای چند متد بنیادی است که از کلاس object به ارث برده است.
متدهای عمومی System.Object
کلاس object چندین متد عمومی دارد، اما چهار مورد از آنها اهمیت ویژهای دارند زیرا اغلب در
کلاسهای مشتقشده بازنویسی (override) میشوند تا رفتار پیشفرض آنها متناسب با نوع جدید تغییر
کند. این چهار متد عبارتند از:
- ToString()
- Equals(object obj)
- GetHashCode()
- GetType()
سه متد اول به صورت virtual تعریف شدهاند، به این معنی که ما میتوانیم و باید آنها را در
کلاسهای خودمان override کنیم. متد چهارم، GetType، مجازی نیست و نمیتوان آن را بازنویسی کرد.
متد ToString()
وظیفهی متد ToString() بازگرداندن یک نمایش رشتهای و قابل خواندن از
شیء فعلی است. پیادهسازی پیشفرض این متد در کلاس object، تنها نام کامل نوع (شامل فضای نام) را
برمیگرداند که معمولاً اطلاعات مفیدی به ما نمیدهد. بنابراین، یکی از اولین کارهایی که هنگام
تعریف یک کلاس جدید انجام میدهیم، بازنویسی این متد است.
Program.cs
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public override string ToString()
{
return $"'{Title}' by {Author}";
}
}
Book myBook = new Book { Title = "1984", Author = "George Orwell" };
Console.WriteLine(myBook);
خروجی این کد به جای نام کلاس، یک رشتهی معنادار خواهد بود:
'1984' by George Orwell
بازنویسی ToString برای دیباگ کردن و لاگگیری بسیار مفید است.
متد Equals(object obj)
این متد برای بررسی برابری دو شیء به کار میرود. پیادهسازی پیشفرض آن در System.Object برای
انواع ارجاعی، برابری ارجاع (Reference Equality) را بررسی میکند؛ یعنی فقط زمانی
true برمیگرداند که دو متغیر به یک شیء واحد در حافظه اشاره کنند. این رفتار معمولاً آن چیزی
نیست که ما میخواهیم. ما اغلب میخواهیم برابری مقدار (Value Equality) را بررسی
کنیم، یعنی دو شیء متفاوت را بر اساس مقادیر فیلدهایشان با هم مقایسه کنیم.
متد GetHashCode()
این متد یک عدد صحیح (هشکد) را برمیگرداند که نمایندهی وضعیت شیء است. این هشکد توسط ساختارهای
دادهی مبتنی بر هش مانند Dictionary و HashSet برای جستجو و ذخیرهسازی بهینه استفاده میشود.
یک قانون بسیار مهم وجود دارد: اگر دو شیء بر اساس متد Equals شما برابر هستند، آنها
باید حتماً GetHashCode یکسانی نیز داشته باشند. بنابراین، هرگاه Equals را
بازنویسی میکنید، باید GetHashCode را نیز بازنویسی کنید.
Program.cs
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public override bool Equals(object obj)
{
if (obj is Point p)
{
return this.X == p.X && this.Y == p.Y;
}
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
در این مثال، ما Equals را طوری بازنویسی کردهایم که دو شیء Point را بر اساس مختصات X و Y
آنها مقایسه کند. سپس GetHashCode را نیز متناسب با آن بازنویسی کردهایم تا از همان پراپرتیها
برای تولید هشکد استفاده کند. کلاس HashCode یک ابزار کمکی مدرن در .NET برای ترکیب
آسان هشکدهاست.
نکته: به یاد بیاورید که نوع record که در درسهای قبل معرفی شد، به طور خودکار
این دو متد را به صورت بهینه برای ما پیادهسازی میکند.
متد GetType()
آخرین متد مهم، GetType است. این متد مجازی نیست و نمیتوان آن را بازنویسی کرد. وظیفهی آن
بازگرداندن یک شیء از نوع System.Type است که حاوی تمام اطلاعات فرادادهای (metadata) در مورد
نوع واقعی شیء در زمان اجراست. این متد در سناریوهای پیشرفتهتری مانند انعکاس
(Reflection) کاربرد دارد که به ما اجازه میدهد تا در زمان اجرا، ساختار یک نوع را
بررسی و با آن تعامل کنیم.
Program.cs
Point p = new Point();
Type pointType = p.GetType();
Console.WriteLine($"The type is: {pointType.FullName}");