219. global namespace

בהמשך לטיפים הקודמים,

לפעמים אנחנו עשויים להיתקל במצב בו יש לנו מספרNamespaceים עם שמות דומים, אבל לא עם אותו שורש.

למשל:

1
2
3
4
5
6
7
namespace First.Second.Third
{
}
namespace Second.Third
{
}

כאשר Second.Third הוא לא Namespace מקונן של First.

נניח שמוגדר טיפוס בSecond.Third ואנחנו רוצים להשתמש בו מהNamespace הראשון:

1
2
3
4
5
6
namespace Second.Third
{
public class FirstClass
{
}
}

אם נשתמש בusing לפני ההגדרה של הNamespace, הדבר אכן אפשרי:

1
2
3
4
5
6
7
8
9
using Second.Third;
namespace First.Second.Third
{
public class SecondClass
{
private FirstClass mThisWorks;
}
}

אבל בלי זה, זה לא כל כך יעבוד:

1
2
3
4
5
6
7
namespace First.Second.Third
{
public class SecondClass
{
private Second.Third.FirstClass mThisWorks;
}
}

נקבל את שגיאת הקימפול הבאה:

Error: The type or namespace name ‘FirstClass’ does not exist in the namespace ‘First.Second.Third’ (are you missing an assembly reference?)

מה קורה כאן? כאשר אנחנו כותבים Second, הקומפיילר חושב שאנחנו מתכוונים לקיצור של First.Second (כי זהו Namespace מקונן), ולכן כאשר כתבנו Second.Third.FirstClass, הוא חושב שהתכוונו לFirst.Second.Third.FirstClass, ובו כמובן הוא לא מוצא את הטיפוס.

באופן דומה, הקומפיילר יכול להתבלבל בין Nested Types וNested Namespaces עם שמות דומים, למשל:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace First
{
public class FirstClass
{
public class InnerClass
{
}
}
}
namespace FirstClass
{
public class InnerClass
{
}
}

אז אם ננסה לגשת לInnerClass של הnamespace ששמו FirstClass מהNamespace ששמו First, לא כל כך נצליח:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace First
{
public class FirstClass
{
public void Test()
{
FirstClass.InnerClass x = new FirstClass.InnerClass();
// First.FirstClass.InnerClass and not FirstClass.InnerClass
}
public class InnerClass
{
}
}
}

מאותה סיבה – מאחר והNamespace מקונן, הקומפיילר חושב שאנחנו מתכוונים בFirstClass.InnerClass לטיפוס שנמצא בתוך הNamespace שלנו.


כדי לפתור את הבעיות האלה, אפשר להשתמש בKeyword ששמו global::, הוא בעצם אומר לקומפיילר לשכוח מהNamespace שבו הוא נמצא, ולחפש את מה שרשמנו בעזרת השם המלא שלו.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace First
{
public class FirstClass
{
public void Test()
{
global::FirstClass.InnerClass x = new global::FirstClass.InnerClass();
// FirstClass.InnerClass
}
public class InnerClass
{
}
}
}
namespace First.Second.Third
{
public class SecondClass
{
private global::Second.Third.FirstClass mThisWorks;
}
}

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

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

מה גם, שCodeDom ספציפית אינו מאפשר להוסיף using מעל הNamespace, אלא רק בתוך הNamespace עצמו, כך שגם הבעיה הראשונה יכולה להתרחש (ואף התרחשה!).

אז מה עושים? אם תסתכלו על הקוד של אובייקט שחולל ע"י כלי של הFramework (למשל, XSD או DataSet), תראו שבכל מקום הוא משתמש בglobal:: ובשמות המלאים, כדי להיות בטוח שהוא ניגש לטיפוס שזה השם המלא שלו, ולא לטיפוס אחר שנמצא בתוך הNamespace בתוכו אנחנו חיים (ואליו הקוד מג’ונרט).

שיהיה המשך יום גלובלי טוב.

שתף