132. Automatic properties and structs

ראינו בעבר (טיפ מספר 89) כי בC# 3.0 נוספה אפשרות להגדיר Properties בצורה יותר נוחה.

הדבר עובד טוב למחלקות, אך האם הוא עובד גם לstructים?

במבט ראשון, נראה שזה עובד:

1
2
3
4
5
6
public struct Point3D
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}

הקוד מתקמפל ועובד.

כעת נקשה על העניין, נהפוך את הsetterים להיות private:

1
2
3
4
5
6
public struct Point3D
{
public int X { get; private set; }
public int Y { get; private set; }
public int Z { get; private set; }
}

עדיין מתקמפל, אבל עכשיו כבר אי אפשר לאתחל את הProperties מבחוץ.

אז נוסיף Constructor שיאתחל לנו אותם:

1
2
3
4
5
6
7
8
9
10
11
12
13
public struct Point3D
{
public Point3D(int x, int y, int z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
public int X { get; private set; }
public int Y { get; private set; }
public int Z { get; private set; }
}

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

Backing field for automatically implemented property ‘Point3D.Z’ must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.
Backing field for automatically implemented property ‘Point3D.Y’ must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.
Backing field for automatically implemented property ‘Point3D.X’ must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.
The ‘this’ object cannot be used before all of its fields are assigned to

מה קורה פה?

זוכרים שאתמול הזכרנו שבConstructor שאינו דיפולטי של struct אנחנו מחויבים לאתחל את כל השדות?

ובכן, הAuto-Properties בעצם יוצרים לנו שדות מאחורי הקלעים, שאותם לא איתחלנו בConstructor. לכן הקוד אינו מתקמפל.

איך נוכל לפתור את הבעיה?

קיימות מספר אופציות:

  • אופציה ראשונה הוא מה שמציע לנו הקומפיילר: נקרא לConstructor הדיפולטי:
1
2
3
4
5
6
7
public Point3D(int x, int y, int z) :
this()
{
this.X = x;
this.Y = y;
this.Z = z;
}

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

  • אופציה שנייה היא להסיר את הConstructor הזה. ככה אי-אפשר לאתחל את הProperties האלה
  • אופציה שלישית היא לעשות מימוש שהוא לא Auto-Property ואז לאתחל ישירות את השדות בערכים האמיתיים שלהם, אבל רק אתחול אחד

הייתי הולך על הפתרון השלישי, אבל כל אחד ומה שנראה לו.

המשך יום מובנה טוב

שתף