بیایید یک تلفنچی را مثال بزنیم؛ این شخص ممکن است بهخاطر فشار شدید کاری در یک روزگرم تابستانی، بهشدت از کوره در رفته باشد و حتی چند تماس را با بدخلقی تمام پاسخ داده باشد، اما وقتی یک تماس شخصی از سوی یکی از اعضای خانوادهاش دریافت میکند، اخلاق خوشی از خود نشان میدهد و صورت دیگری از خود را بروز میدهد. این قابلیت، یعنی چندریختی2 یکی از ویژگیهای پیادهشده در برنامهنویسی شیءگرا است.
انواع مختلفی از چندریختی ممکن است وجود داشته باشد، یکی از آنها که مثال زدیم، بازنویسی رفتار3 است. یکی دیگر از انواع بازنویسی، بازنویسی عملگرها4 است که بهآن بیشتر خواهیم پرداخت.
هدف از بازنویسی عملگرها، تغییر رفتار عملگرهاست، تا جایی که عملگرها با توجه نوع عملوندها رفتاری متفاوتی از خود نشان بدهند. برخی از این عملوندها را خود زبان برنامهنویسی بازنویسی کرده است، اما برخی دیگر را برنامهنویس بهدلخواه خود بازنویسی میکند.
بگذارید یک مثال بزنیم تا بهتر مشخص شود چه کاری میخواهیم انجام بدهیم. عملگر جمع (+) را فرض کنید. در زبان سیشارپ اگر 2 عملوند عدد صحیح از نوع int باشد، خروجی int خواهد بود و 2 عدد را جمع میکند و حاصل را بر میگرداند، حالا اگر 2 عملوند از نوع رشته متنی باشند، چه؟ در اینجا زبان سیشارپ یک بازنویسی عملگر درونی دارد و در آن، 2 رشته ورودی را با هم ترکیب میکند و نتیجه بهصورت یک رشتهمتنی باز میگردد، مثلا اگر بنویسیم: "Jamejam" + " Click!" خروجی عبارت Jamejam Click! خواهد بود، در ادامه نحوه بازنویسی عملگرها را برای کلاسی که خودمان تعریف کردیم بررسی میکنیم.
پیش از اینکه نحوه بازنویسی کردن عملگرها را توضح دهیم، لازم است ببینیم کدام عملگرها قابلیت بازنویسی دارند:
عملگرهای دودویی (عملگرهایی که به دو عملوند (Operand) برای اجرا شدن نیاز دارند)، مانند: + (جمع)، - (تفریق)، *، /، %، &، |، ^، «« و »» .
عملگرهای یگانی (عملگرهایی که تنها به یک عملوند نیاز دارند)، مانند: + (مثبت)، - (منفی)، !، ~، ++، --، true و false.
بقیه عملگرها قابلیت باز نویسی ندارند ولی بعضی از آنها را میتوان بهصورت دیگری بازنویسی کرد؛ مثلا عملگر += خلاصه عملگر + است، که خود + قابلیت بازنویسی را دارد یا عملگر () (تبدیل نوع یا cast) ، قابل بازنویسی را ندارد، ولی شما با استفاده از explicit و implicit که هر دو از keywordهای سیشارپ هستند میتوانید آنرا بازنویسی کنید.
کار خود را با یک مثال شروع میکنیم. ما یک کلاس به اسم Matrix تعریف کردیم. این کلاس عملیات خاصی را روی یک ماتریس انجام میدهد. مثلا دترمینان یک ماتریس را حساب میکند، یا 2ماتریس را جمع یا ضرب میکند و ... . حال فرض کنید بدون استفاده از بازنویسی عملگرها بخواهیم این کلاس را بنویسیم. برای جمع 2 ماتریکس، 2 روش وجود دارد، نخست اینکه یک متد استاتیک تعریف کنیم تا 2 ماتریس را جمع کند و حاصل را بهصورت یک ماتریس برگرداند. این متد بهصورت زیر خواهد بود:
System.Math.Matrix mx1 =
new System.Math.Matrix(4,5);
System.Math.Matrix mx2 =
new System.Math.Matrix(4,5);
System.Math.Matrix result =
System.Math.Matrix.Add(mx1, mx2);
این روش اشتباه نیست، اما بگذارید کمی آن را بهتر بنویسیم. در کلاس ماتریکس متد Add را از حالت استاتیک در میآوریم، و بهجای دریافت 2 پارامتر ورودی، یک پارامتر ورودی بهآن میدهیم و با خودش جمع میکنیم:
mx1.Add(mx2);
بههرحال این هم یک روش است که بهنظر سادهتر و منطقیتر میرسد. حتی از این هم میتوان زیباتر نوشت. اینجا همانجایی است که بازنویسی عملگرها وارد صحنه میشود:
mx1 = mx1 + mx2;
اگر بتوانیم ماتریکسها را همانطور که روی کاغذ مینویسیم و جمع میزنیم، در کد خود بنویسیم، هم خواندن کد برای ما سادهتر میشود، هم در کار تیمی جلوه بهتری دارد.
ما چه کار خواهیم کرد؟ عملگر + (جمع) را برای کلاس ماتریس خودمان بازنویسی میکنیم و به کامپایلر نشان میدهیم که اگر 2 عملوند عملگر + از جنس ماتریس بود، فلان کار را انجام بده. که این فلان کار یعنی 2 ماتریس را با هم جمع کن و خروجی را از نوع ماتریکس برگردان. خوب این هم کار ما را خیلی سادهتر کرد، البته بیشتر برای کسانی که از کلاس ما استفاده می کنند ساده شده است، حال بررسی میکنیم ببینیم این عمل چگونه اتفاق میافتد یعنی کامپایلر از کجا میفهمد که باید هنگام برخورد با +، بهسراغ متد خاصی برود و آن را اجرا کند؟ بیایید متدی را تعریف کنیم:
public class Matrix {
public static Matrix operator +
(Matrix mx1, Matrix mx2)
return mx1.Add(mx2); }
همانطور که میبینید، در این جا ما عملگر + را بهصورت عملگری دوگانه تعریف کرده که 2 پارامتر mx1 و mx2 را دریافت میکند و mx1 را با mx2 جمع کرده و نتیجه را باز میگرداند (با استفاده از متد فرضی Add).
و در نهایت کد بسیارسادهای خواهیم داشت. اما فراموش نکنید، استفاده از بازنویسی عملگرها، کد را بهشدت ساده میکند، اما همین سادگی میتواند کار دست برنامهنویس بدهد. بهفرض، اگر عملگر + را برای یک پارامتر از هر نوعی و یک پارامتر از نوع int بگیرید بهخاطر تقدم و تاخر پارامترها، C+2 مجاز است، اما 2+C غیر مجاز است چون در ترتیب آرگومانها، بازنویسی ندارد. این اشتباهات ساده ممکن است صدمات بزرگی به کد نوشته شده بزند و آن را کاملا غیرقابل اعتماد بکند.
پینوشتها
1. Object Oriented
2. Polymorphism
3. Method Overloading
4. Operator Overloading
امیربهاالدین سبطالشیخ
مرور بزرگ ترین جنجال های تاریخ جام جهانی (8)