33. new() constraint

ראינו בפעם הקודמת שיש דרך להחזיר את הערך הדיפולטי של Type גנרי.

לפעמים אנחנו רוצים לעשות יותר מזה – ממש לאתחל משתנה מהType הפנימי.

למשל, נניח שיש לנו ממשק של נקודה במרחב

1
2
3
4
5
6
public interface IPoint
{
double X { get; set; }
double Y { get; set; }
double Z { get; set; }
}

ויש לנו אוסף של נקודות (זה יכול היה להיות למשל קוביה או תיבה).

היינו רוצים שתהיה לנו פונקציית Add כדלהלן

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PointCollection<TPoint> : List<TPoint>
where TPoint : IPoint
{
public void Add(double x, double y, double z)
{
TPoint point = new TPoint();
point.X = x;
point.Y = y;
point.Z = z;
this.Add(point);
}
}

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

הבעיה היא שלא בהכרח קיים Constructor דיפולטי לTPoint, ולכן זה לא מתקמפל.

המציאו בשבילנו משהו שפותר את הבעיה הזאת: נוכל להוסיף לConstraint את התנאי

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PointCollection<TPoint> : List<TPoint>
where TPoint : IPoint, new()
{
public void Add(double x, double y, double z)
{
TPoint point = new TPoint();
point.X = x;
point.Y = y;
point.Z = z;
this.Add(point);
}
}

זה אומר לקומפיילר שTPoint יכול לקבל רק Typeים שיש להם Constructor דיפולטי.

מספר הערות:

  1. הnew() חייב להופיע אחרון ברשימה של הConstraintים
  2. למרבה הצער המנגנון לא מספיק חזק כדי שנוכל לציין את הפרמטרים שאנחנו מעוניינים שיופיעו בConstructor, למשל היה יותר טבעי שהמחלקה והממשק יראו ככה:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface IPoint
{
double X { get; }
double Y { get; }
double Z { get; }
}
public class PointCollection<TPoint> : List<TPoint>
where TPoint : IPoint, new(double, double, double)
{
public void Add(double x, double y, double z)
{
this.Add(new TPoint(x, y, z));
}
}

הקטע קוד לעיל לא מתקמפל. הדבר מונע מאיתנו לשים readonly properties בinterfaceים.

ראו גם טיפים 322-323 המסבירים מה קורה מאחורי הקלעים כשאנחנו קוראים לnew על פרמטר גנרי.

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

שתף