176. Typed safe method & property names

ראינו בעבר כיצד אפשר להשתמש בReflection כדי להשיג MethodInfo/PropertyInfo של מתודה או Property נתון. (ראו גם טיפים 146,147,158)

הבעיה בעסק הזה היא שאנחנו מקבלים הרבה stringים Hard-Coded שמייצגים את השם של האלמנט שאנחנו מעוניינים להשיג בReflection.

מה הבעיה בזה? ובכן, זה מתקמפל, אבל לא בהכרח עובד. אם השמות של המתודות/Properties האלה ישתנו, אז הקוד עדיין יתקמפל, אבל נעוף בזמן ריצה.

בניגוד לTypeים שיש בשבילם את הKeyword ששמו typeof, אין מקבילה למתודות/Properties המאפשרת לנו להשיג אותם בצורה שהיא Typed safe.

אפשר להשתמש בExpression Trees כדי להשיג את השמות האלה בצורה אחרת:

1
2
3
4
5
6
7
8
Expression<Func<Person, int>> myProperty =
(Person x) => x.Age;
MemberExpression myPropertyBody =
(MemberExpression) myProperty.Body;
PropertyInfo ageInfo = (PropertyInfo) myPropertyBody.Member;
// Same result as PropertyInfo ageInfo = typeof(Person).GetProperty("Age");

נוכל אפילו לכתוב מתודה סטטית גנרית נחמדה שתעשה את זה בצורה יפה:

1
2
3
4
5
6
7
8
9
10
11
12
public static PropertyInfo GetProperty<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
MemberExpression expressionBody =
expression.Body as MemberExpression;
if (expressionBody == null)
{
return null;
}
return expressionBody.Member as PropertyInfo;
}

ולקרוא לה כך:

1
2
PropertyInfo ageInfo = GetProperty((Person x) => x.Age);
// Same result as PropertyInfo ageInfo = typeof(Person).GetProperty("Age");

בקשר למתודות, נוכל לעשות משהו דומה:

1
2
3
4
5
6
7
8
9
10
11
12
public static MethodInfo GetMethod<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
{
MethodCallExpression expressionBody =
expression.Body as MethodCallExpression;
if (expressionBody == null)
{
return null;
}
return expressionBody.Method;
}

ולהשתמש בה כך:

נניח שיש לנו את המתודה הזאת:

1
2
3
4
5
6
7
public class Circle : Shape
{
public Square GetBoundedSquare(double angle, Color color)
{
// ...
}
}

אזי נשתמש במתודה GetMethod כך:

1
2
3
MethodInfo getBoundedSquareInfo =
GetMethod((Circle x) => x.GetBoundedSquare(default(int), default(Color)));
// Same result as MethodInfo getBoundedSquareInfo = typeof(Circle).GetMethod("GetBoundedSquare");

שימו לב שאין פה stringים שהם Hard Coded. במידה והפונקציה תחליף שם או חתימה, הקוד לא יתקמפל.

במידה ונרצה לבצע אותו הדבר למתודות שהן סטטיות/שאינן מחזירות ערך, נצטרך להוסיף Overloadים מתאימים.

שימו לב לעוד דבר – מאחר וזה Expression Tree והוא צריך להיות ביטוי שמתקמפל – אנחנו חייבים להעביר ערכים לפונקציה. אפשר לכתוב סתם ערכים, למשל:

1
2
3
MethodInfo getBoundedSquareInfo =
GetMethod((Circle x) => x.GetBoundedSquare(3, Color.Red));
// Same result as MethodInfo getBoundedSquareInfo = typeof(Circle).GetMethod("GetBoundedSquare");

מה שדי דוחה, אבל אפשר להפוך את החיסרון ליתרון! מאחר ואנחנו חייבים לציין ערכים – נציין את הדיפולטים של הערכים שאנחנו מחפשים (ראו למעלה), וככה נקבל את החתימה המדויקת שאנחנו מחפשים! 😃

שימו לב שבשיטה הזאת אנחנו לא מסוגלים לגשת לMemberים שהם לא חשופים לנו (למשל, private מחוץ למחלקה שלנו). יש לזה גם יתרונות, כמובן.

סופ"ש מצוין

שתף