در این مقاله، با مفهوم بازنویسی متد در سی شارپ «#Method Overriding in C» آشنا خواهیم شد و نحوه استفاده از کلیدواژههای مختلف مانند virtual و override برای بازنویسی متدها را بررسی میکنیم. همچنین به بررسی محدودیتها و قوانینی که در این زمینه وجود دارند خواهیم پرداخت تا بهتر درک کنیم که چگونه میتوانیم این قابلیت را در کدهای خود به طور مؤثر به کار ببریم.
مقدمه
در زبان برنامهنویسی سی شارپ #C، یکی از مفاهیم کلیدی که به توسعهدهندگان امکان میدهد کدهایی انعطافپذیرتر و مقیاسپذیرتر بنویسند، بازنویسی متد «Method Overriding» است. این ویژگی از ویژگیهای مهم چندریختی پویا «Dynamic Polymorphism» محسوب میشود که به کلاسهای مشتقشده اجازه میدهد تا رفتارهای متدهای کلاس پایه را تغییر دهند. بازنویسی متد این امکان را فراهم میکند که یک متد در کلاس پایه تعریف شده، اما در کلاس مشتقشده پیادهسازی جدیدی داشته باشد. این ویژگی به برنامهنویسان این امکان را میدهد که کدهای خود را به گونهای طراحی کنند که قابلیت استفاده مجدد و گسترش آسانتری داشته باشند.
بازنویسی متد در سی شارپ چیست؟
بازنویسی متد در سی شارپ مشابه تابع مجازی «Virtual Function» در سی پلاسپلاس ++C است. این تکنیک به ما امکان میدهد که توابع یک کلاس دیگر (کلاس پایه) را در کلاس مشتق شده فراخوانی کنیم. ایجاد یک متد در کلاس مشتق شده با همان امضا «Signature» مانند متد موجود در کلاس پایه، بازنویسی متد نامیده میشود.
به زبان ساده، بازنویسی قابلیتی است که به یک زیرکلاس «Subclass» یا کلاس فرزند اجازه میدهد یک پیادهسازی خاص از متدی را ارائه دهد که قبلاً در یکی از ابرکلاسها «Super Classes» یا کلاسهای والد تعریف شده است. هنگامی که یک متد در زیرکلاس دارای همان نام، همان پارامترها (یا امضا) و همان نوع بازگشتی (یا زیرنوع آن) مانند متدی در ابرکلاس باشد، گفته میشود که متد در زیرکلاس، متد موجود در ابرکلاس را بازنویسی «Override» کرده است.
بازنویسی متد در سی شارپ یکی از روشهایی است که چندریختی زمان اجرا «Run Time Polymorphism» یا چندریختی پویا «Dynamic Polymorphism» را در #C فراهم میکند.
متدی که توسط یک دستور override بازنویسی میشود، متد پایهی بازنویسیشده «Overridden Base Method» نامیده میشود. یک متد override، پیادهسازی جدیدی از عضوی است که از یک کلاس پایه به ارث برده شده است. متد پایهای که بازنویسی میشود باید مجازی «Virtual»، انتزاعی «Abstract» یا خودش بازنویسیشده «Override» باشد.
مثال
class base_class { public void PgS(); } class derived_class : base_class { public void PgS(); } class Main_Method { static void Main() { derived_class d = new derived_class(); d.PgS(); } }
در مثال بالا، کلاس پایه در کلاس مشتقشده به ارث برده شده است و متد ()PgS که دارای همان امضا در هر دو کلاس است، بازنویسی «Override» شده است.
انواع کلیدواژه برای بازنویسی متد در سی شارپ
در زبان برنامهنویسی #C، میتوانیم از ۳ نوع کلیدواژه برای بازنویسی متدها استفاده کنیم:
- virtual: برای تعریف متدهای قابل بازنویسی در کلاس پایه
- override: برای بازنویسی متدهای virtual یا abstract در کلاس مشتقشده
- base: برای دسترسی به اعضای کلاس پایه از داخل یک کلاس مشتقشده
کلیدواژه virtual در سی شارپ
تعدیلکننده «Modifier» یا کلیدواژه virtual در متد کلاس پایه استفاده میشود. این کلیدواژه برای تعدیل یک متد در کلاس پایه بهکار میرود تا امکان بازنویسی آن متد در کلاس مشتقشده فراهم شود.
کلیدواژه override در سی شارپ
تعدیلکننده «Modifier» یا کلیدواژه override در متد کلاس مشتقشده استفاده میشود. این کلیدواژه برای تعدیل یک متد مجازی «Virtual» یا انتزاعی «Abstract» در کلاس مشتقشده استفاده میشود که در کلاس پایه تعریف شده است.
مثال:
class base_class { public virtual void PgS(); } class derived_class : base_class { public override void PgS(); } class Main_Method { static void Main() { derived_class d = new derived_class(); d.PgS(); base_class b = new derived_class(); b.PgS(); } }
در مثال بالا، ابتدا d به شیء کلاس derived_class اشاره میکند و متد ()PgS مربوط به کلاس derived_class را فراخوانی میکند. سپس، b به عنوان یک ارجاع از کلاس base استفاده میشود، اما شیء کلاس derived_class را در خود نگه میدارد و متد ()PgS مربوط به کلاس derived_class را فراخوانی میکند.
در مثال بالا، متد ()PgS برای بازنویسی متد در کلاس مشتقشده، از کلاس پایه اجازه میگیرد.
مثال ۱: بازنویسی متد در سی شارپ بدون استفاده از کلیدواژههای virtual و override
// C# program to demonstrate the method overriding // without using 'virtual' and 'override' modifiers using System; // base class name 'baseClass' class baseClass { public void show() { Console.WriteLine("Base class"); } } // derived class name 'derived' // 'baseClass' inherit here class derived : baseClass { // overriding new public void show() { Console.WriteLine("Derived class"); } } class PStore { // Main Method public static void Main() { // 'obj' is the object of // class 'baseClass' baseClass obj = new baseClass(); // invokes the method 'show()' // of class 'baseClass' obj.show(); obj = new derived(); // it will invokes the method // 'show()' of class 'baseClass' obj.show(); } }
خروجی
Base class Base class
توضیح: در این برنامه، شیء obj دو بار کلاس baseClass را فراخوانی کرده و متد ()show را از کلاس baseClass اجرا میکند. برای جلوگیری از این مشکل، از کلیدواژههای virtual و override استفاده میکنیم.
مثال ۲: بازنویسی متد در سی شارپ با استفاده از کلیدواژههای virtual و override
// C# program to illustrate the use of //'virtual' and 'override' modifiers using System; class baseClass { // show() is 'virtual' here public virtual void show() { Console.WriteLine("Base class"); } } // class 'baseClass' inherit // class 'derived' class derived : baseClass { //'show()' is 'override' here public override void show() { Console.WriteLine("Derived class"); } } class PStore { // Main Method public static void Main() { baseClass obj; // 'obj' is the object // of class 'baseClass' obj = new baseClass(); // it invokes 'show()' // of class 'baseClass' obj.show(); // the same object 'obj' is now // the object of class 'derived' obj = new derived(); // it invokes 'show()' of class 'derived' // 'show()' of class 'derived' is overridden // for 'override' modifier obj.show(); } }
خروجی
Base class Derived class
کلیدواژه base در سی شارپ
کلیدواژه base برای دسترسی به اعضای کلاس پایه از داخل کلاس مشتقشده استفاده میشود. این کلیدواژه عمدتاً برای دسترسی به سازندهها «Constructors»، متدها «Methods» یا توابع کلاس پایه به کار میرود.
نکات مهم درباره کلیدواژه base
- این کلیدواژه نمیتواند در داخل متدهای static استفاده شود.
- base مشخص میکند که هنگام ایجاد یک نمونه از کلاس مشتقشده، کدام سازنده از کلاس پایه باید فراخوانی شود.
کاربردهای کلیدواژه base
- فراخوانی متدها یا توابع کلاس پایه از کلاس مشتقشده.
- فراخوانی داخلی سازنده کلاس پایه در زمان وراثت.
مثال ۳: بازنویسی متد در سی شارپ با استفاده از کلیدواژه base
// C# program to show the use of 'base' // keyword in method overriding using System; // base class public class web { string name = "ProgramStore"; // 'showdata()' is member method, // declare as virtual public virtual void showdata() { Console.WriteLine("Website Name: " + name); } } // derived class // class 'web' is inherits // class 'stream' class stream : web { string s = "Computer Science"; //'showdata()' is overridden // in derived class public override void showdata() { // Calling 'showdata()' of base // class using 'base' keyword base.showdata(); Console.WriteLine("About: " + s); } } class PStore { // Main Method static void Main() { // 'E' is object of class stream // also works as object of // class 'web' stream E = new stream(); // it first invokes 'showdata()' // of class 'web' then it invokes // 'showdata()' of class 'stream' E.showdata(); } }
خروجی
Website Name: ProgramStore About: Computer Science
مثال ۴: نحوه استفاده از کلیدواژه base برای فراخوانی سازنده کلاس پایه از کلاس مشتقشده
// C# program to show how base keyword // specifies the calling of base-class // constructor from the derived class // when derived class instances are created using System; // base class public class clssA { int n1, n2; // default constructor public clssA() { Console.WriteLine("Default Constructor Invoked"); } // parameterized constructor public clssA(int i, int j) { // construct values n1 = i; n2 = j; Console.WriteLine("Parameterized Constructor Invoked"); Console.WriteLine("Invoked Values are: " + n1 + " and " + n2); } } // derived class public class DerivedClass : clssA { // This constructor will instantiate // 'clssA()' [no argument constructor] // using 'base' keyword public DerivedClass() : base() { } // This constructor will instantiate // 'clssA(int i, int j)' [parameterized // constructor] using 'base' keyword public DerivedClass(int i, int j) : base(i, j) { } // Main Method static void Main() { // invoke no argument constructor DerivedClass d1 = new DerivedClass(); Console.WriteLine(); // invoke parameterized constructor DerivedClass d2 = new DerivedClass(10, 20); } }
خروجی
Default Constructor Invoked Parameterized Constructor Invoked Invoked Values are: 10 and 20
هنگامی که یک نمونه «Instance» از کلاس مشتقشده ایجاد میشود، میتوان با استفاده از کلیدواژه base مشخص کرد که کدام سازنده از کلاس پایه باید فراخوانی شود.
در مثال بالا، هنگام ایجاد شیء از کلاس مشتقشده، سازنده کلاس پایه بهطور خودکار توسط base اجرا میشود.
مثال ۵: استفاده از کلیدواژه base برای فراخوانی سازنده کلاس پایه و متد آن از کلاس مشتقشده
در مثال زیر، هنگام ایجاد شیء از کلاس مشتقشده، هم سازنده کلاس پایه و هم متد آن توسط کلیدواژه base فراخوانی میشوند.
// C# program to show how 'base' keyword specifies // the base-class constructor that called from // derived class and also calling a method 'swap' // from derived class using base keyword using System; // base class public class clssA { public int n1, n2; // default constructor public clssA() { Console.WriteLine("In clssA 'no argument constructor' invoked"); } // parameterized constructor public clssA(int i, int j) { // construct values n1 = i; n2 = j; Console.WriteLine("in clssA 'parameterized constructor' invoked"); Console.WriteLine("the invoked values are " + n1 + " and " + n2); Console.WriteLine(); } public virtual void swap() { Console.WriteLine("swap function of base class(clssA) invoked"); Console.WriteLine("Before swap num1 = {0} and num2 = {1}", n1, n2); // swapping int t = n1; n1 = n2; n2 = t; Console.WriteLine("After swap num1 = {0} and num2 = {1}", n1, n2); } } // derived class public class DerivedClass : clssA { // This constructor will instantiate // 'clssA' [no argument constructor] // using 'base' keyword public DerivedClass() : base() { } // This constructor will instantiate // 'clssA' [parameterized constructor] // using 'base' keyword public DerivedClass(int i, int j) : base(i, j) { } public override void swap() { // it access the swap function of // 'clssA' using 'base' keyword base.swap(); Console.WriteLine(); Console.WriteLine("Swap function of derived class invoked"); Console.WriteLine("Before swap num1 = {0} and num2 = {1}", n1, n2); // swapping int t = n1; n1 = n2; n2 = t; Console.WriteLine("After swap num1 = {0} and num2 = {1}", n1, n2); } // Main Method static void Main() { // invoke no argument constructor DerivedClass d1 = new DerivedClass(); Console.WriteLine(); // invoke parameterized constructor DerivedClass d2 = new DerivedClass(10, 20); // calling swap function d2.swap(); } }
خروجی
In clssA 'no argument constructor' invoked in clssA 'parameterized constructor' invoked the invoked values are 10 and 20 swap function of base class(clssA) invoked Before swap num1 = 10 and num2 = 20 After swap num1 = 20 and num2 = 10 Swap function of derived class invoked Before swap num1 = 20 and num2 = 10 After swap num1 = 10 and num2 = 20
نکته: بازنویسی متد در سی شارپ تنها در کلاسهای مشتقشده امکانپذیر است. زیرا متد در کلاس مشتقشده از کلاس پایه بازنویسی میشود. یعنی متد در کلاس پایه تعریف شده و در کلاس مشتقشده بازنویسی میشود.
یک متد غیر مجازی «non-virtual» یا متد استاتیک «static» نمیتواند بازنویسی شود. متدهایی که مجازی «virtual» نیستند یا استاتیک «static» هستند، بهطور مستقیم نمیتوانند توسط کلاسهای مشتقشده بازنویسی شوند.
متد override و متد virtual باید دارای یکسانترین سطح دسترسی «access level modifier» باشند.
این یعنی اگر متد virtual در کلاس پایه دارای دسترسی public باشد، متد override در کلاس مشتقشده نیز باید دسترسی public داشته باشد.
جمع بندی
در این مقاله، مفهوم بازنویسی متد در سی شارپ #C را مورد بررسی قرار دادیم و نشان دادیم که چگونه این ویژگی به توسعهدهندگان این امکان را میدهد که متدهای کلاسهای پایه را در کلاسهای مشتقشده بازنویسی کنند و رفتار آنها را تغییر دهند. با استفاده از کلیدواژههای virtual و override، میتوانیم کدهایی انعطافپذیر و مقیاسپذیر ایجاد کنیم که به راحتی قابل گسترش و نگهداری باشند.
همچنین به محدودیتها و قوانینی مانند نیاز به داشتن یکسان بودن سطح دسترسی بین متد virtual و متد override و این که متدهای غیر مجازی یا استاتیک نمیتوانند بازنویسی شوند، اشاره کردیم. این قوانین به ما کمک میکنند تا از بروز اشتباهات جلوگیری کرده و استفاده بهینه از این ویژگی را داشته باشیم.
در نهایت، بازنویسی متد در سی شارپ یکی از ابزارهای قدرتمند برای پیادهسازی چندریختی پویا (Dynamic Polymorphism) است که در طراحی سیستمهای پیچیده و انعطافپذیر نقش مهمی ایفا میکند. استفاده صحیح از این ویژگی میتواند به برنامهنویسان کمک کند تا کدهایی قابل فهم، قابل نگهداری و مقیاسپذیر بنویسند.