197. implicit operator

ראינו פעם קודמת כיצד אפשר ליצור אופרטורים משלנו.

יש כמה אופרטורים מיוחדים שלא כל כך טריוואלי שאפשר לעשות להם Operator overload.

באחד מהם נפגשנו בעבר (טיפ מספר 2).

אחד האופרטורים הנוספים הוא אופרטור ששמו implicit. זה נראה כמו משהו חדש, אבל בעצם כולנו כבר מכירים אותו:

נניח שיש לנו את הקוד הבא:

1
2
int seven = 7;
float number = seven; // 7.0

מה בדיוק קורה כאן? ובכן, אם נזכר בטיפ על ההבדל בין Value types וReference types נשים לב שבValue types כמו int וfloat אין ירושה.

לכן מה שקורה כאן זו לא "סתם" המרה ממחלקת בן למחלקת אב, אלא ממש איזשהי המרה המאפשרת לנו להסב את המשתנה הראשונה לסוג של המשתנה השני באופן לא טבעי.

מאחר והסבה זו אינה מאבדת מידע, היא נעשית באופן "שקוף", כלומר implicitly. אם ההסבה הייתה מאבדת מידע (למשל מfloat לint), אז היא לא הייתה שקופה, והיינו צריכים לבצע אותה באופן מפורש.

זה מתבצע באמצעות קריאה של הקומפיילר לפונקציה מתאימה המבצעת הסבה זו.

אנחנו יכולים לעשות דברים כאלה בעצמנו: נניח שיש לנו מחלקה המייצגת מעלות ומחלקה המייצגת רדיאנים:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Radians
{
public double Angle
{
get;
private set;
}
public Radians(double angle)
{
this.Angle = angle;
}
}
public class Degrees
{
public double Angle
{
get;
private set;
}
public Degrees(double angle)
{
this.Angle = angle;
}
}

נוכל לכתוב הסבה שהיא implicit ביניהם. נכתוב למשל בתוך המחלקה של הרדיאנים:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Radians
{
// ...
public static implicit operator Radians(Degrees degrees)
{
return new Radians(degrees.Angle * Math.PI / 180.0);
}
public static implicit operator Degrees(Radians degrees)
{
return new Degrees(degrees.Angle * 180.0 / Math.PI);
}
}

ואז נוכל לבצע המרות באופן שקוף:

1
2
3
Degrees thirtyDegrees = new Degrees(30.0);
Radians radians = thirtyDegrees;
Console.WriteLine(radians.Angle); // Pi/6

שימו לב שזה גם מסוכן, כיוון שאנחנו יכולים לכתוב קוד שלא התכוונו לכתוב ומתקמפל, למשל להחזיר בטעות מפונקציה שאמורה להחזיר מעלות, רדיאנים.

למשל נניח שיש לנו פונקציה שאמורה להחזיר את סכום הזוויות במשולש. נניח שבטעות רשמנו משהו כזה:

1
2
3
4
public static Degrees SumOfTriangleAngles()
{
return new Radians(180);
}

הדבר מתקמפל, אבל לא זאת הייתה הכוונה שלנו, כיוון שעכשיו יוחזר לנו מהפונקציה 180 רדיאנים במעלות, במקום 180 מעלות.

מתי בכל זאת כדאי להשתמש?

כשאתם רוצים לעשות ששני אובייקטים יהיו "שקולים" בלי לרשת אחד מהשני.

למשל, נניח שיש לכם אובייקט משלכם שמייצג שגיאה שאפשר גם לכתוב לLog ולעשות איתו עוד דברים, אז אפשר לעשות הסבה implicit מException לאובייקט שלכם.

עוד דוגמה היא כזאת – נניח שיש לכם אובייקט שמייצג כתובת ונניח שיש לו ייצוג מחרוזתי לכתובת זו (למשל בעזרת הפונקציה ToString). אפשר ליצור הסבה implicit בין המחרוזת המייצגת את האובייקט לאובייקט שלכם. למשל:

1
IPAddress localHost = "127.0.0.1";

בכל אופן, חשוב לדאוג שההסבה לא תאבד מידע, מאחר והיא מתבצעת באופן בלתי גלוי לעין.

שימו לב שלא נוכל ליצור הסבות ממחלקת אב או הסבות למחלקות אב – אלו שמורות ע"י הקומפיילר.

המשך יום מוסב לטובה

שתף