228. Dynamic member overload resolution

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

לפני כמה זמן סיפרתי קצת על Multiple dynamic dispatchואמרתי שבC# 4.0 אפשר לקבל את ההתנהגות הנ”ל בצורה קלה יותר.

איך עושים את זה?

תזכורת: יש לנו את הOverloadים הבאים:

1
2
3
4
public static void InscribeInShape(Square innerShape, Ellipse circumscribedShape)
public static void InscribeInShape(Ellipse innerShape, Square circumscribedShape)
public static void InscribeInShape(Triangle innerShape, Circle circumscribedShape)
public static void InscribeInShape(Circle innerShape, Triangle circumscribedShape)

ואנחנו רוצים שהפונקציה הבאה:

1
public static void InscribeInShape(Shape innerShape,Shape circumscribedShape)

תדע לנתב בזמן ריצה את הצורות לOverload המתאים ביותר. (ראו גם טיפ מספר 224)

איך עושים זאת?

הFeature של Dynamic binding גם מאפשר לנו למצוא את הOverload הטוב ביותר של מתודה.

1
2
3
4
public static void InscribeInShape(Shape innerShape, Shape circumscribedShape)
{
InscribeInShape((dynamic)innerShape, (dynamic)circumscribedShape);
}

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

משהו כזה (לא ממש יפה)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void InscribeInShape(Shape innerShape, Shape circumscribedShape)
{
if (InscribeInShapeCallSite == null)
{
InscribeInShapeCallSite =
CallSite<Action<CallSite, Type, object, object>>.Create(
Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "InscribeInShape", null, typeof (ShapeUtilities),
new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.IsStaticType |
CSharpArgumentInfoFlags.UseCompileTimeType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
InscribeInShapeCallSite.Target.Invoke(InscribeInShapeCallSite, typeof (ShapeUtilities), innerShape,
circumscribedShape);
}

נעזוב לרגע את מה שקורה מאחורי הקלעים – יש בעיה מסוימת בפתרון שכתבתי פה:

אם נעביר לפונקציה פרמטרים בלי overload מתאים, אז הOverload הכי מתאים של הפונקציה תהיה הפונקציה עצמה.

זה גורם לרקורסיה אינסופית, מה שאנחנו ממש לא רוצים.

מה אפשר לעשות במקום? פתרון לגיטימי הוא לעשות שהמימוש של

1
public static void InscribeInShape(Shape innerShape, Shape circumscribedShape)

יהיה פשוט:

1
2
3
4
public static void InscribeInShape(Shape innerShape, Shape circumscribedShape)
{
throw new ArgumentException("No suitable overload was matched");
}

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

1
2
3
4
public static void InscribeShapeInOther(Shape innerShape, Shape circumscribedShape)
{
InscribeInShape((dynamic)innerShape, (dynamic)circumscribedShape);
}

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

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

שתף