Overriding in CSharp

همانطور که قبلا گفتم تمامی کلاس ها در سی شارپ چه بخواهند و چه نخواهند از کلاسی به نام object به ارث می روند و در نتیجه خصوصیات این کلاس به آن ها ارث می رسد. به عنوان مثال متد ToString که در تمامی کلاس هایی که ما ایجاد می کنیم وجود دارد و وقتی روی یک شیء متد ToString را فراخوانی می کنیم یک String از آن شیء در اختیار ما قرار می دهد که به صورت پیش فرض این رشته نام کامل کلاس شامل Namespace.Class می باشد.

کد:
Person p = new Person();
p.Name = "Ali";
p.Age = 20;
Console.WriteLine(p);
    // ConsoleApplication13.Person

در مثال بالا من یک شیء از کلاس Person ایجاد کرده و بعد از ست کردن نام و سن دستور چاپ آن شیء را از طریق متد WriteLine ارسال کرده ام. با توجه به اینکه متد WriteLine در موقع چاپ اشیاء به یک String نیاز دارد متد ToString را بر روی شیء من فراخوانی خواهد کرد. در نتیجه یک رشته از شیء من چاپ خواهد شد که نام کامل کلاس یعنی : ConsoleApplication13.Person می باشد.

به شکل زیر دقت کنین:



همانطور که می بینین متد ToString از کلاس پدر که کلاس object است به من به ارث رسیده است. اما رفتار این متد رفتاری نیست که من نیاز داشته باشم به این معنا که من نیاز دارم وقتی متد ToString را روی اشیایی از جنس کلاس Person (که من ایجاد کردم) فراخوانی می شود به جای نام کامل کلاس (Qualified Name) اطلاعات آن را (یعنی نام و سن) در اختیار من قرار دهد. به این معنی که می خواهم رفتارهای کلاس پدر را تغییر دهم که اصطلاحا به این کار Overriding می گویند.

برای اینکه من بتوانم رفتار کلاس پدر را تغییر دهم باید متد مورد نظرم را override کنم که این کار با استفاده از عبارت override و نوشتن مجدد متد با رفتار مورد نظر خودمون امکان پذیر می باشد:

کد:
public class Person
{
       public string Name;
       public int Age;

      override public string ToString()
      {
         return string.Format("Name:{0}, Age:{1}",Name,Age);
       }
}
بعد از نوشتن این تکه کد اگر دوباره کدی که در ابتدا نوشتیم را اجرا کنیم با رفتار جدید متد ToString که در واقع چاپ نام و سن می باشد مواجه خواهید شد.


Virtual Methods in CSharp

اصولا وقتی یک کلاس ایجاد می کنیم باید در نظر داشته باشیم که اگر این کلاس توسط کلاس دیگری به ارث رفت کدامیک از متد ها یا Property های آن توسط فرد دیگری استفاده خواهند شد و اگر کسی در کلاس جدید نیاز به تغییر رفتار داشت این امکان را در اختیار وی قرار دهیم.


فرض بفرمائید که من یک کلاس به نام Person با دو Propertyی نام و سن و یک Method به نام Print که نام و سن را چاپ می کند ایجاد کرده ام. حالا می خواهم کاری کنم که کلاس هایی که از کلاس Person به ارث می روند بتوانند رفتار متد Print را override کنند. برای اینکه این امکان را در اختیار فرزندانم (کلاس هایی که از من به ارث می روند) قرار دهم باید در تعریف متد از عبارت virtual استفاده کنم. به کد کلاس Person دقت کنین:


کد:
public class Person
{
public string Name;
public int Age;
public virtual void Print()
{
Console.WriteLine("Name: {0}, Age: {1}", Name, Age);
}
}

حالا کلاس Emp را از کلاس Person به ارث می برم و یک فیلد جدید به نام Salary به آن اضافه می کنم و انتظار دارم که با override کردن متد Print کاری کنم که وقتی Print روی اشیایی از جنس Emp فراخوانی می شوند علاوه بر نام و سن , حقوق را نیز چاپ کند.

کد:
public class Emp : Person
{
               public decimal Salary;

               override public void Print()
               {
                  Console.WriteLine("Name:{0}, Age: {1}, Salary: {2}",Name,Age,Salary);
               }
}
با توجه به کد بالا در صورتیکه که این کد را برای تست بنویسم باید علاوه بر نام و سن , حقوق کارمند را هم چاپ نماید:

کد:
Emp e = new Emp();
e.Name = "Reza";
e.Age = 25;
e.Salary = 240000;
e.Print();


!! نکته بسیار مهم در مورد Overriding این است که در صورتیکه Reference شما به یک شیء از جنس پدر نیز باشد , کامپایلر سی شارپ بدون توجه به نوع Reference به ماهیت شیء توجه کرده و متد مربوطه را چاپ می نمایند. به کد زیر دقت کنین:


کد:
Emp e = new Emp();
e.Name = "Saeid";
e.Age = 44;
e.Salary = 54000;

// در این خط از کد عملیات UpCast به سادگی انجام می شود
Person p = e;
// فراخوانی متدی که در کلاس پدر وجود داشته و در کلاس فرزند override شده است
p.Print();
با اینکه reference ما به شیء از جنس پدر(Person) می باشد ولی به دلیل override شدن در کلاس فرزند , پیاده سازی متد فرزند یعنی چاپ نام , سن و حقوق اجرا می شود.