170. Factory by Attributes

היום נראה דוגמה נחמדה לשימוש בAttributeים.

נניח שאנחנו רוצים לממש Factory של צורות, כלומר מחלקה אשר בונה לנו instance של צורה לפי השם שלה.

ראינו שני סוגי מימושים בטיפים שמספרם 50 ו139.

תזכורת: המימוש הכי פשוט הוא משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ShapeFactory
{
public static Shape CreateByName(string name)
{
switch (name)
{
case "Circle":
{
return new Circle();
}
case "Triangle":
{
return new Triangle();
}
case "Square":
{
return new Square();
}
default:
{
return null;
}
}
}
}

אבל הוא גם המימוש הכי מכוער.

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

אז יאללה, לעבודה!


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static class ShapeFactory
{
public static Shape CreateByName(string name)
{
// TODO : implement this
return null;
}
[ShapeBuilder("Triangle")]
private static Shape BuildTriangle()
{
return new Triangle();
}
[ShapeBuilder("Square")]
private static Shape BuildSquare()
{
return new Square();
}
[ShapeBuilder("Circle")]
private static Shape BuildCircle()
{
return new Circle();
}
}

המתודה CreateByName תדע לאיזו מתודה (למתודה כזו אני אוהב לקרוא Builder) ללכת לפי הstring שמצוין בAttribute מעליה.

תחשבו שבמתודות הBuilderים, יכולה להיות לוגיקה יותר מסובכת…

זה מזכיר קצת את טיפ מספר 50, אבל גם קצת שונה.

ראשית עלינו לממש את הAttribute. זה די קל:

1
2
3
4
5
6
7
8
9
10
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class ShapeBuilderAttribute : Attribute
{
public string Name { get; private set; }
public ShapeBuilderAttribute(string name)
{
this.Name = name;
}
}

שימו לב שאנחנו שמים AttributeUsage של מתודות בלבד.

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

המימוש שלה הוא לא קשה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static Shape CreateByName(string name)
{
Type factoryType = typeof (ShapeFactory);
MethodInfo[] allStaticMethods =
factoryType.GetMethods(BindingFlags.Static |
BindingFlags.NonPublic |
BindingFlags.Public);
IEnumerable<MethodInfo> builderMethods =
from method in allStaticMethods
let builderAttributes = method.GetCustomAttributes(typeof(ShapeBuilderAttribute), true)
from ShapeBuilderAttribute builderAttribute in builderAttributes
where builderAttribute.Name == name
select method;
MethodInfo requestedBuilder = builderMethods.Single();
return (Shape)requestedBuilder.Invoke(null, null);
}

הפונקציה עשויה להראות מסובכת, אבל כל מה שקורה כאן זה שמתבצע חיפוש על כל הפונקציות הסטטיות של המחלקה ואנחנו מחפשים פונקציה שיש מעליה ShapeBuilderAttribute עם Name שזהה לname שקיבלנו.

אחרי זה אנחנו קוראים לה ועושים הסבה לShape. ראו גם טיפים מספר 95,97,147,148,154,167,168.

כעת נוכל לקרוא לפונקציה:

1
Shape triangle = ShapeFactory.CreateByName("Triangle");

די מגניב.


יש פה חסרון שיש פה קריאה איטית בReflection. הראתי פעם (טיפ מספר 151) כיצד ניתן לשפר זאת באמצעות Delegate.CreateDelegate.

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

הרעיון הוא ליצור Dictionary<string, Func<Shape>> סטטי שיכיל מיפוי של השמות לBuilderים (כמו בטיפ מספר 50) ולמלא אותו בפונקציות שיש מעליהן Attribute בConstructor הסטטי באמצעות הפונקציה Delegate.CreateDelegate. אולי אני אראה איך עושים את זה מתישהו.

המשך ערב בר סגולה

שתף

169. AttributeUsageAttribute

ראינו בפעמים הקודמות כיצד ניתן לכתוב Attribute משלנו. בנוסף, פעם קודמת ראינו כיצד אנחנו יכולים לתשאל את הMemberInfoים שלנו לגבי הAttributeים שנמצאים מעליהם.

בד”כ לא נרצה שיוכלו לשים את הAttribute שלנו מעל כל “סוג קוד”, אלא נרצה להגביל את האפשרויות לסוגי קוד ספציפיים, למשל מתודות, מחלקות או מאפיינים (Properties).

למשל, נניח שיש לנו Attribute שמציין שמתודה מסוימת רצה בצורה אסינכרונית, היינו רוצים להגביל אותו למתודות ולא לדברים אחרים (כדוגמת מחלקות/Properties)

בשביל זה הומצא הAttribute ששמו AttributeUsageAttribute. Attribute זה מאפשר לנו לשלוט בדרך שבה משתמשים בAttribute שלנו, כלומר על איזה סוגי קוד אפשר לשים אותו ועוד מספר מאפיינים שנראה.

איך זה נראה? מעל מחלקת Attribute שלנו, אנחנו יכולים לשים Attribute זה. בConstructor שלו אנחנו מעבירים ערך של Enum ששמו AttributeTargets המציין מי היעד האפשרי של הAttribute הזה (כלומר, על מי אפשר לשים אותו).

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

1
2
3
4
5
[AttributeUsage(AttributeTargets.Method)]
public class AsyncAttribute : Attribute
{
// ...
}

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

1
2
3
4
5
6
[Async]
public int NotAMethod
{
get;
private set;
}

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

Attribute ‘Async’ is not valid on this declaration type. It is only valid on ‘method’ declarations.

הEnum ששמו AttributeTargets הוא מסוג Flags, ולכן נוכל לשרשר מספר יעדים:

1
2
3
4
5
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class AsyncAttribute : Attribute
{
// ...
}

כדאי להסתכל על התוכן של הEnum ולהיות מופתעים מהיעדים האפשריים לAttribute. למשל, ידעתם שאפשר לשים Attribute על ReturnValue או Parameter?


בנוסף, AttributeUsage מאפשר לנו לציין עוד שני דברים (דרך Properties):

האם אפשר לשים את הAttribute יותר מפעם אחת על Member:

1
2
3
4
5
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class AsyncAttribute : Attribute
{
// ...
}

ברגע ששמנו AllowMultiple = false, לא נוכל לשים את הAttribute הזה יותר מפעם אחת באותו מקום.

1
2
3
4
5
[Async]
[Async]
public void HeavyMethod()
{
}

בזמן קימפול נקבל

Error: Duplicate ‘Async’ attribute

הדבר האחרון שאפשר להגדיר בAttribute זה האם הוא עובר בירושה.

למשל, אם שמנו את הAttribute שלנו מעל מתודה וירטואלית, ואנחנו דורסים את המתודה אצל מחלקה יורשת, האם אנחנו צריכים לשים את הAttribute גם במחלקה היורשת, או שזה יחלחל לבד?

את זה נוכל לקבוע באמצעות הProperty ששמו Inherited. אם נשים לו את הערך true, הAttribute יחלחל גם בירושה.

אנחנו יכולים לקבוע אם אנחנו מעוניינים שיתחשב בירושה כזו בקריאה לפונקציה GetCustomAttributes שראינו אתמול. (זה הtrue שהעברתי אתמול)

אם לא נשים AttributeUsage מעל הAttribute שלנו, הערך Inherited יהיה true ושל AllowMultiple יהיה false.


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

כדי לענות על שאלה זו, נסתכל על הMetadata שלו:

1
2
3
4
5
6
[Serializable, ComVisible(true),
AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class AttributeUsageAttribute : Attribute
{
// ...
}

כפי שאנחנו רואים, יש מעליו AttributeUsage עם הפרטים הנ"ל. רקורסיבי ודי מגניב 😃

המשך יום בר סגולה

שתף

168. GetCustomAttributes

בפעם הקודמת ראינו כיצד אנחנו יכולים ליצור Attribute משלנו ולקשט את הקוד שלנו בעזרתו.

כדי שלAttribute תהיה השפעה על איך הקוד שלנו מתנהג, נוכל להשתמש בפונקציה GetCustomAttributes של MemberInfo (ראו גם טיפ מספר 161):

לדוגמה, אם נמשיך עם הטיפוסים מהדוגמה של אתמול:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MemberInfo myClassType = typeof(MyClass);
object[] attributes =
myClassType.GetCustomAttributes(true);
foreach (Attribute currentAttribute in attributes)
{
PeanutAttribute peanutAttribute =
currentAttribute as PeanutAttribute;
if (peanutAttribute != null)
{
Console.WriteLine(peanutAttribute.Address);
Console.WriteLine(peanutAttribute.Description);
Console.WriteLine(peanutAttribute.Length);
}
}
// http://en.wikipedia.org/wiki/MyClass
// My class is the best class
// 10

באופן דומה, מאחר וגם MethodInfo, PropertyInfo יורשים מMemberInfo, נוכל להריץ את פונקציה זו עליהן, למשל:

1
2
3
4
[PeanutAttribute("This method does something", 3)]
public void SomeMethod(int givenNumber, string givenString)
{
}

הרצת הקוד תניב את התוצאות הבאות:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MethodInfo someMethodInfo =
typeof(MyType).GetMethod("SomeMethod");
object[] attributes =
someMethodInfo.GetCustomAttributes(true);
foreach (Attribute currentAttribute in attributes)
{
PeanutAttribute peanutAttribute =
currentAttribute as PeanutAttribute;
if (peanutAttribute != null)
{
Console.WriteLine(peanutAttribute.Address);
Console.WriteLine(peanutAttribute.Description);
Console.WriteLine(peanutAttribute.Length);
}
}
//
// This method does something
// 3

(אם אתם לא זוכרים כיצד משיגים MethodInfo, תוכלו לקרוא את טיפ מספר 147)

בעצם, מה שקורה כאן זה שהוספנו Metadata משלנו.

כעת נשאר להשתמש בכלי זה בחוכמה כדי לעשות דברים יפים 😃

נראה דוגמאות בהמשך.

המשך יום בר סגולה

שתף

167. Custom Attribute

ראינו פעמים קודמות דוגמאות לAttributeים מהFramework.

באופן דומה, אנחנו יכולים ליצור Attribute משלנו. Attribute כזה נקרא Custom Attribute.

כדי לעשות זאת עלינו לרשת מהמחלקה Attribute.

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

אז הנה:

1
2
3
4
public class PeanutAttribute : Attribute
{
// ...
}

עכשיו נוכל להצמיד אותו לMember/Typeים שלנו:

1
2
3
4
5
6
7
8
9
10
[PeanutAttribute()]
public void SomeMethod(int givenNumber, string givenString)
{
}
[Peanut]
public class MyClass
{
// ..
}

שימו לב שבגלל שהAttribute שלנו עונה על הקונבנציה, אנחנו פטורים מכתיבת המילה Attribute (ראו גם טיפ מספר 164)

נוכל גם לשים לו Properties משלנו או Constructor לא דיפולטי:

1
2
3
4
5
6
7
8
9
10
11
12
public class PeanutAttribute : Attribute
{
private string Description { get; set; }
private int Length { get; set; }
public PeanutAttribute(string description, int length)
{
Description = description;
Length = length;
}
// ...
}

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

1
2
3
4
5
6
7
8
9
10
[PeanutAttribute("This method does something", 3)]
public void SomeMethod(int givenNumber, string givenString)
{
}
[Peanut("My class is the best class", 10)]
public class MyClass
{
// ..
}

ואם יש לנו Properties שהם public נוכל גם אותם לאתחל בAttribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PeanutAttribute : Attribute
{
private string Description { get; set; }
private int Length { get; set; }
public string Address { get; set; }
public PeanutAttribute(string description, int length)
{
Description = description;
Length = length;
}
// ...
}

למשל

1
2
3
4
5
[Peanut("My class is the best class", 10, Address ="http://en.wikipedia.org/wiki/MyClass")]
public class MyClass
{
// ..
}

עד כאן החגיגה…


למרבה הצער, יש גם מספר מגבלות במחלקות שהן Attributeים. ראשית, הן לא יכולות להיות גנריות.

שנית, לא נוכל לאתחל בConstructor (או בעזרת Property) טיפוס שהוא לא מהצורה הבאה:

  • Primitive – כלומר bool, byte, char,double, float, int, long, short, string
  • Enum
  • Type – אותו נאתחל באמצעות typeof
  • מערך חד מימדי מאחד הסוגים מעלה

שבוע בר סגולה טוב

שתף

166. DebuggerHiddenAttribute

Attribute נוסף שקיים בFramework הוא Attribute ששמו DebuggerHiddenAttribute.

בדומה לObsoleteAttribute שמשפיע על איך הקומפיילר מתייחס לקוד שלנו שמסומן בו, DebuggerHiddenAttribute משפיע על איך הDebugger מתייחס לקוד שמסומן בAttribute זה.

לProperty/מתודה/Constructor שנשים מעליו את הAttribute ששמו DebuggerHiddenAttribute, לא נכנס בזמן דיבוג (בStep Into). גם לא נוכל לשים בו Breakpoint (כלומר, אם נשים בו Breakpoint, הוא לא יעצור בו).

לדוגמה,

1
2
3
4
5
[DebuggerHidden]
public static void CanNotBreakMe()
{
Console.WriteLine("Break here if you're a man!");
}

אם נשים Breakpoint בתוך הפונקציה, או ננסה לעשות Step-into אליה, לא נצליח. הBreakpoint לא יעצור.

למה זה טוב?

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

לכן נפוץ לשים מעל מתודות של מחלקות מחוללות Attribute זה.

גילוי נאות: הכותב לא השתמש בAttribute זה במחלקות שחולל.

סוף שבוע לא חבוי

שתף

165. ObsoleteAttribute

הכרנו בפעמים הקודמות Attributeים.

כעת נראה דוגמה לAttribute ששמו ObsoleteAttribute.

הAttribute מאפשר לנו לציין כי איזושהי מחלקה/מתודה/קוד אחר כבר אינו נתמך.

למשל, נניח כתבנו מחלקה ואנחנו לא מעוניינים שמשתמשים בקוד שלנו ישתמשו בה – נוכל לשים את הAttribute:

1
2
3
4
5
[Obsolete]
public class LegacyClass
{
// ...
}

בזמן קימפול אם מישהו ישתמש במחלקה זו הוא יקבל Warning:

‘LegacyClass’ is obsolete

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

בנוסף, נוכל להוסיף תיאור שיופיע בWarning:

1
2
3
4
5
[Obsolete("Use NewClass instead")]
public class LegacyClass
{
// ...
}

‘LegacyClass’ is obsolete: ‘Use NewClass instead’

בנוסף, נוכל לציין שאם מישהו ישתמש במחלקה הזו הוא יקבל Error ולא Warning:

1
2
3
4
5
[Obsolete("Use NewClass instead", true)]
public class LegacyClass
{
// ...
}

‘LegacyClass’ is obsolete: ‘Use NewClass instead’

זה בעיקר טוב למקומות שבהם רוצים לשנות API, אבל בלי לשבור API ישן.

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

שתף

164. Placing attributes

אתמול הכרנו קצת את המושג של Attribute.

ראינו שאנחנו יכולים לשים, למשל, SerializableAttribute מעל למחלקה Person בצורה הבאה:

1
2
[SerializableAttribute()]
public class Person

הקונבנציה של הFramework היא שהשמות של כל הAttributeים מסתיימים במילה Attribute. לכן, מאפשרים לנו להשמיט את המילה Attribute בבואנו לשים אחד:

1
2
[Serializable()]
public class Person

יותר מזה, אם אנחנו מאתחלים Attribute באמצעות הConstructor הדיפולטי שלו, אנחנו לא צריכים לציין את הקריאה באמצעות ה():

1
2
[Serializable]
public class Person

בנוסף, אם אנחנו צריכים לאתחל Properties של הAttribute שלנו, אנחנו יכולים לעשות זאת בצורה הבאה:

1
2
[XmlRoot("Person", Namespace = "http://personNamespace.org")]
public class Person

כאן אנחנו קוראים לConstructor שמקבל string ואחר כך מאתחלים את הProperty ששמו Namespace בערך "http://personNamespace.org&quot;. (מזכיר Object Initializer, ראו גם טיפ מספר 87)


לבסוף, נניח ואנחנו מעוניינים לאתחל מספר Attributeים, נוכל לעשות זאת במספר דרכים:

דרך אחת היא פשוט לרשום אותם אחד מתחת לשני:

1
2
3
4
[Serializable]
[CLSCompliant(false)]
[XmlRoot("Person", Namespace = "http://personNamespace.org")]
public class Person

דרך שנייה היא להשתמש בסוגריים המרובעים לכמה Attributeים:

1
2
3
[Serializable, CLSCompliant(false)]
[XmlRoot("Person", Namespace = "http://personNamespace.org")]
public class Person

או ככה למשל:

1
2
[Serializable, CLSCompliant(false), XmlRoot("Person", Namespace ="http://personNamespace.org")]
public class Person

אם הAttributeים בעלי ארגומנטים, יותר קריא לשים כל אחד בסוגריים משלו, אבל לפעמים זה גם קריא לשים אותם ביחד (למרות שבד"כ זה לא)

חג עצמאות שמח

שתף

163. System Attribute

עד כה ראינו כיצד ניתן לקרוא Metadata של טיפוסים ומתודות.

לפעמים אנחנו מעוניינים להוסיף Metadata משלנו לטיפוסים ומתודות. לדוגמה, נניח שיש לנו instance של מחלקה ואנחנו מעוניינים לייצר ייצוג Stringי שלה בצורה דינאמית, ע”י הדפסת הערכים של כל הProperties שהיא מכילה.

נוכל לעשות את זה בצורה הבאה:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static string ToString(object instance)
{
Type instanceType = instance.GetType();
PropertyInfo[] properties = instanceType.GetProperties();
List<string> propertiesValues = new List<string>();
foreach (PropertyInfo property in properties)
{
object propertyValue =
property.GetValue(instance, null);
propertiesValues.Add(string.Format("{0}={1}",
property.Name,
propertyValue));
}
return string.Join(Environment.NewLine,
propertiesValues.ToArray());
}

עכשיו, נניח שאנחנו רוצים להדפיס רק חלק מהProperties. איך נוכל להחליט איזה Property להדפיס ואיזה לא?

אפשרות אחת היא לפי קונבנציה – רק Properties שהשם שלהם לפי קונבנציה מסוימת, למשל מסתיים בPrintable יודפסו. אלא, שזה מלכלך לנו את הקוד.

מה שהיינו רוצים לעשות בעצם זה לציין איכשהו בMetadata את העובדה שאנחנו מעוניינים להתעלם מהProperty שלנו בצורה כזו או אחרת.


כמובן, זו דוגמה אחת מתוך רבות לסיבה שאנחנו עלולים לרצות להוסיף מידע לMetadata שלנו.

למזלנו, השפה מאפשרת לנו לעשות דבר כזה, זאת באמצעות מושג שנקרא Attribute.

Attribute הוא בעצם טיפוס שיורש מהמחלקה Attribute. מה שמיוחד בו זה שאנחנו יכולים לשים אותו מעל Memberים שלנו וכך "לקשט" אותם.

למשל קיים הAttribute ששמו SerializableAttribute. Attribute זה ניתן לשים מעל מחלקות וכך לציין שמחלקה כלשהי ניתן לסריאליזציה.

איך זה נראה?

1
2
[SerializableAttribute()]
public class Person

קצת הסבר: בתוך הסוגריים המרובעים אנחנו מאתחלים את הAttribute. זאת ע"י קריאה לConstructor הדיפולטי – אלה הסוגריים המצוינים העגולים. שימו לב שאין צורך בקריאה לאופרטור new , אלא זה מתבצע בצורה אוטומטית.

הFramework יודע לקרוא את הMetadata ולהתייחס למחלקות שיש מעליהן Attribute זה בצורה נכונה.

ונזכור את כולם

שתף

162. DeclaringMethod property

בהמשך לטיפ של אתמול,

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static T[] Reverse<T>(T[] array)
{
T[] result =
new T[array.Length];
int currentIndex = array.Length - 1;
foreach (T current in array)
{
result[currentIndex--] = current;
}
return result;
}

אם נבצע את השורות הבאות

1
2
3
4
5
MethodInfo reverseMethod =
typeof (SuperMethods).GetMethod("Reverse");
Type parameterType =
reverseMethod.GetGenericArguments()[0]; // T

נקבל T.

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

1
2
3
4
5
6
MethodBase parameterMethod = parameterType.DeclaringMethod;
if (parameterMethod == reverseMethod)
{
// true
}

סופ"ש מוצהר טוב

שתף

161. MemberInfo DeclaringType and ReflectedType

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

הכרנו את MethodInfo ואת PropertyInfo. ראינו שאם אנחנו קוראים לפונקציה GetMethods או GetProperties של Type אנחנו מקבלים גם Methods או Properties ששייכים לטיפוסים שאנחנו יורשים מהם.

למשל, ראינו שאם אנחנו קוראים לGetMethods אנחנו מקבלים גם 4 פונקציות מObject:

1
2
3
4
// System.String ToString()
// Boolean Equals(System.Object)
// Int32 GetHashCode()
// System.Type GetType()

נניח שאנחנו יוצרים טיפוס משלנו ודורסים את ToString. האם נוכל לדעת עפ"י GetMethods האם הפונקציה היא של הטיפוס שלנו או של טיפוס אחר?


כדי לענות על שאלה זו – נציג את MemberInfo – זהו טיפוס שכל המחלקות מReflection שפגשנו יורשות ממנו. יורשות ממנו MethodInfo, PropertyInfo ואפילו Type.

לטיפוס זה שני Properties מעניינים:

ReflectedType וDeclaringType. הראשון מייצג את הType שעשינו עליו Reflection כדי לקבל את הMemberInfo, למשל:

1
2
3
4
5
6
7
8
9
10
11
12
IEnumerable<MethodInfo> publicMethods =
typeof (Person).GetMethods();
IEnumerable<Type> reflectedTypes =
publicMethods.Select(method => method.ReflectedType).Distinct();
foreach (Type reflectedType in reflectedTypes)
{
Console.WriteLine(reflectedType.Name);
}
// Person

זה יכול להיות שימושי אם נכתוב, למשל, פונקציה המקבלת MethodInfo ותרצו לדעת מאיזה Type היא התקבלה ע"י Reflection.

הProperty השני מייצג את הType אליו שייך הMemberInfo. אם, למשל, נריץ את הקוד הבא על הטיפוס Person מאתמול:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
IEnumerable<MethodInfo> publicMethods =
typeof (Person).GetMethods();
foreach (MethodInfo methodInfo in publicMethods)
{
Console.WriteLine("{0} - {1}", methodInfo, methodInfo.DeclaringType);
}
// Int32 get_Age() - Person
// Void set_Age(Int32) - Person
// System.String get_FirstName() - Person
// Void set_FirstName(System.String) - Person
// System.String get_LastName() - Person
// Void set_LastName(System.String) - Person
// Void Talk() - Person
// System.String ToString() - System.Object
// Boolean Equals(System.Object) - System.Object
// Int32 GetHashCode() - System.Object
// System.Type GetType() - System.Object

נשים לב שהמתודות שהוספנו בPerson אכן שייכות לPerson, והשאר שייכות לobject.

אם נדרוס את ToString, למשל, נוכל לקבל משהו כזה:

1
2
3
4
5
6
MethodInfo toString = typeof (Person).GetMethod("ToString");
if (toString.DeclaringType == typeof(Person))
{
// true
}

המשך יום הצהרה משוקף לטובה

שתף