יום שבת, 5 ביוני 2010

העמסת אופרטורים וסימטריה בC#

במאמר זה אסביר על איך להשתמש בהעמסת אופרטורים וכיצד לשמור על הסימטריה שלהן.
הקדמה:
אז בתור פתיח אסביר קודם מהם אופרטורים, למי שלא יודע אופרטורים זה סימני פעולה. מה הכוונה סימני פעולה? הכוונה היא לסימנים כמו < (גדול) , > (קטן) , <= (גדול שווה) ,+ ,- , * , / וכו'.. כל סימן שמבצע פעולה על אופרנד אחד או יותר (אופרנד הוא המשתנה/קבוע שעליו מתבצעת הפעולה).
רק כדי להבהיר:
5+3
הפלוס הוא האופרטור, והמספרים הם האופרנדים.

רק אעיר 4 הערות נוספות חשובות:
1.יש אופרטורים אונרים(סימן פעולה המתבצע על אופרנד אחד),אופרטורים בינארים(סימן פעולה המתבצע על שני אופרנדים), ואפילו טרינרים(סימן פעולה שמתבצע על שלוש אופרנדים). דוגמה לאופרטור בינארי הוא *, ודוגמה לאופרטור אונרי הוא ++ (העלאה באחד כידוע..).
2.לא ניתן לשנות עם העמסת(דריסת) אופרטורים את הקדימויות של האופרטורים. דהיינו אם אני מבצע פעולה כזאת a+b *c תמיד תמיד יתבצע פעולת ה* ("הכפל") לפני פעולת ה+ ("החיבור").
3.לא ניתן להמציא אופרטורים חדשים, נניח לא תוכלו לקבוע כי ^ היא העלאה בחזקה.. ניתן רק להעמיס(לדרוס) אופרטורים שבשפה.
4.ישנם אופרטורים שלא ניתן לדרוס, לדוג' האופרטור נקודה (גישה לחברים). וזה לטובה כמובן, מכיוון שזה יכול ליצור סיבוכים וטעויות קריטיות אילו היה ניתן. ומן הסתם בשונה מC++ אתם לא דורסים את [],לכן יש דבר הנקרא indexers בC#... כנ"ל לגבי =, לא דורסים כי יש דימוי להשמה באמצעות מאפיינים (properties). אך לא ארחיב עליהם מיפני שאז אגלוש מהנושא.. אולי במאמר אחר :).


נפלא.. עכשיו אנחנו יודעים מה זה מה, מי נגד מי, ואפשר להתקדם.
גוף:
העמסת אופרטורים היא כאמור החלפת אופרטורים קיימים (חוץ מהמגבלות שציינתי להעיל). העמסת אופרטורים דומה מאוד להעמסת שיטות (methods) , אך יש לרשום את המילה operator לצד סימן האופרטור שאתם מעוניינים להכריז במקום שם הפונקציה.

לדוגמה: מבנה (struct) בשם Hour:
struct Hour
{
public int value;
public Hour(int value)
{
this.value=value;
}
public static Hour operator+ (Hour lhs,Hour rhs)
{
return new Hour(lhs.value+rhs.value);
}
}
להלן שימוש במופע המבנה:
Hour firstHour=new Hour(2);
Hour secondHour=new Hour(3);
Hour thirdHour=firstHour+secondHour;
כפי שאתם רואים, ניתן כעת לחבר מופעים אלו בלי להגדיר לאיזה חבר לגשת וכו'.. כמה נוח :).
אולי כעת זה נראה לכם קצת טיפשי ומיותר, אבל תחשבו שהיה עליכם לבנות מחלקה וקטור תלת מימדי או מבנים/מחלקות יותר מסובכות, ובכל פעם בתוכנית שהייתם רוצים לחבר את הווקטורים הייתם צריכים לחבר באופן ידני את הווקטורים או לחילופין לעשות פעולה "מסובכת" של חיבור המבנה/מחלקה שלכם באופן ידני, במקום שהמהדר יעשה זאת עבורכם וכל מה שעליכם לעשות הוא להעמיס פעם אחת את האופרטור.


אופרטורים סימטריים-
אז ראיתם איך להכריז על אופרטור ולהעמיסו, אך איך שומרים על כך שאופרטורים יהיו סימטריים? מהי הכוונה בכלל אופרטורים סימטריים?
הכוונה היא לאפשר חיבור בשני צידי המטבע. אתן דוגמה למה מעט קשה להסביר את זה במילים...
אם תרצו לחבר נניח את הטיפוס הפרמיטיבי int יחד עם המבנה שלנו אתם תעשו כך:
public static Hour operator+(Hour lhs,int rhs)
{
return new Hour(lhs.value+rhs);
}
ואם תעשו דבר כזה :
int a=5;
Hour b=new Hour(2);
Hour c=b+a;

יהיה תיקני לחלוטין, אך מה היה קורה אם הייתם הופכים את הסדר? עושים c=a+b ?
הייתם מקבלים שגיאת קומפלציה, למה? כי אין לכם סימטריה והמהדר לא מכיר העמסה שהצד השמאלי שלו הוא int והצד הימני הוא מבנה Hour.
אז ניתן לפתור את זה בכך שתעמיסו את הצורה ההפוכה.. אך ניתן לעשות את זה בצורה יותר אלגנטית.
הדרך היותר אלגנטית היא להמיר בצורה מרומזת את int לHour. כך שבכל פעם שיחברו int עם Hour, לא משנה באיזה סדר הint יומר באופן אוטומטי לHour.
<כאן יהיה הסבר על המרה מרומזת...>



אין תגובות:

הוסף רשומת תגובה