217. Nested types

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

למשל, נניח שיש לנו מבנה של עץ, אזי עץ מורכב מצמתים (Nodes) שכל אחד מהם מכיל (חלק מ)המידע הבא: הערך בצומת, הבנים של הצומת, האבא של צומת.

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

בנוסף, יכול להיות שאנחנו לא מעוניינים לחשוף את המושג של צומת לכל העולם, ואולי גם לא למחלקות “החברות” שלנו (כלומר, שנמצאות באותו Assembly או בAssemblyים חברים, מה שinternal מאפשר לנו), אלא רק למחלקה של העץ.

פתרון אפשרי לבעיות אלה הוא המושג של Nested Types.

מה זה בכלל Nested Type? מדובר בטיפוס מקונן בטיפוס אחר: ההגדרה מתבצעת בצורה הבאה:

1
2
3
4
5
6
7
8
9
10
public class MyClass
{
public class MyInnerClass
{
}
public interface IMyInterface
{
}
}

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

1
2
MyInnerClass myInnerClass = new MyInnerClass(); // Doesn't compile
MyClass.MyInnerClass myInnerClass = new MyClass.MyInnerClass(); // compiles

אפשר כמובן לעשות דברים כאלה כדי שהשורה הראשונה כן תעבוד:

1
using MyInnerClass = MyNamespace.MyClass.MyInnerClass;

(ראו גם טיפ מספר 13)

בנוסף, בניגוד להגדרות של מחלקות וממשקים רגילים שיכולים רק לקבל את הAccess Modifier של public או internal, טיפוסים מקוננים יכולים לקבל כל Access Modifier:

private פשוט אומר שהטיפוס המקונן נגיש רק בתוך המחלקה, וprotected אומר שאפשר לגשת לטיפוס המקונן גם דרך המחלקות הבנות של המחלקה. (נחשו לבד מה אומר protected internal)

הדבר הזה בעצם מאפשר לנו הסתרה יותר ספציפית של מחלקות (מאשר להפוך אותן לinternal או public)

בנוסף, מחלקה מקוננת יכולה להנות מהיכולת לגשת לMemberים הפרטיים של המחלקה המכילה אותה, לדוגמה:

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
public class MyType
{
private int mSecret = 3;
public void UseNestedType()
{
MyNestedType nestedType = new MyNestedType(this);
nestedType.TellSecret();
}
private class MyNestedType
{
private MyType mInstance;
public MyNestedType(MyType instance)
{
mInstance = instance;
}
public void TellSecret()
{
Console.WriteLine("The secret is {0}", mInstance.mSecret);
}
}
}

אז הקוד הבא ידפיס

1
2
MyType typeInstance = new MyType();
typeInstance.UseNestedType(); // The secret is 3

אז למה זה טוב? כמו שכתבתי, יש שתי סיבות טובות להשתמש בטיפוסים מקוננים:

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

השנייה היא כדי להסתיר את הטיפוס מרוב העולם, יותר ממה שמאפשר לנו שימוש בModifier בשם internal.

המשך יום מקונן טוב.

שתף