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 עם הפרטים הנ"ל. רקורסיבי ודי מגניב 😃

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

שתף