ادامه درس شانزدهم
تعریف و یا کنترل موارد استفاده از یک صفت
AttributeUsage یکی از کلاسهای از پیش تعریف شده در زبان است که با استفاده از آن میتوانیم موارد استفاده از صفتی را که تولید کردهایم را کنترل کنیم.
این کلاس دارای سه property مختلف است که میتوان آنها را به هنگام استفاده صفت شخصی تنظیم نمود و مورد استفاده قرار داد.
ValidOn
با استفاده از این property میتوانیم مشخص کنیم که صفت تولید شده توسط ما، بر روی کدام یک از عناصر برنامه قابل اعمال هستند. اطلاعات این عناصر از AttributeTarget گرفته میشود و میتوان عناصر مختلف را بوسیله OR بیتی با یکدیگر ترکیب نمود.
AllowMultiple
با استفاده از این property میتوان مشخص کرد که آیا میتوان از این صفت بیش از یکبار بر روی یک عنصر برنامه استفاده کرد یا نه.
Inherited
با استفاده از این property میتوان قوانین ارثبری این صفت را کنترل نمود. با استفاده از این property میتوان مشخص کرد که آیا کلاسی که از کلاسی که صفت بر روی آن اعمال شده، ارث بری میکند نیز، صفت بر رویش اعمال میشود یا نه و یا به عبارتی صفت در کلاس مشتق شده نیز مورد ارثبری قرار میگیرد یا نه.
حال با استفاده از موارد گفته شده در بالا، میخواهیم این مطالب را بر روی صفتی که خودمان تولید کردیم اعمال نماییم. مثال 7-16 را بررسی نمایید.
مثال 7-16
using System;
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false, Inherited = false ]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
در ابتدا به AttributeTargets.Class توجه نمایید. این مشخص میکند که صفت Help تنها بر روی کلاسها قابل اعمال است و در صورتیکه از آن بر روی عنصری به غیر از کلاس استفاده نماییم خطایی رخ خواهد داد. بنابراین کد زیر، خطایی تولید خواهد کرد :
[Help("this is a do-nothing class")]
public class AnyClass
{
[Help("this is a do-nothing method")] //error
public void AnyMethod()
{
}
}
و کد خطای تولید شده بشکل زیر خواهد بود :
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
توجه کنید که با استفاده از AttributeTargets.All به صفت Help این امکان را میدهیم تا بر روی تمامی عناصر موجود اعمال شود. لیست کامل عناصر مجاز نیز بشرح زیر است :
حال به AllowMultiple = false توجه نمایید. با استفاده از این کد، به صفت Help اجازه میدهیم تا تنها یکبار بر روی عنصری از برنامه اعمال شود. پس کد زیر تولید خطا مینماید :
[Help("this is a do-nothing class")]
[Help("it contains a do-nothing method")]
public class AnyClass
{
[Help("this is a do-nothing method")] //error
public void AnyMethod()
{
}
}
و کد خطای تولید شده نیز بصورت زیر است :
AnyClass.cs: Duplicate 'Help' attribute
در نهایت نیز به بررسی Inherited میپردازیم. با استفاده از این ویژگی، معین میکنیم درصورتیکه کلاس دیگری بخواهد از روی کلاسی که صفت بر روی آن اعمال شده ارثبری نماید، آیا این صفت بر روی آن کلاس نیز اعمال شود یا نه. در صورتیکه مقدار این ویژگی برابر با True باشد، کلاس مشتق شده نیز از صفت ارثبری مینماید. برای یک مثال میتوانیم حالت زیر را در نظر بگیریم :
[Help("BaseClass")]
public class Base
{
}
public class Derive : Base
{
}
تمامی حالتهای مختلف ترکیب این سه ویژگی بصورت زیر است :
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]
استفاده از پارامترهای Positional و Named در صفتهای شخصی
همانطور که در قبل نیز اشاره شد، پارامترهای Positional پارامترهای سازنده صفت هستند و در هر بار استفاده از صفت باید لحاظ شوند. حال برای بررسی میخواهیم پارامترهایی به صفت Help خود اضافه نماییم.
مثال 8-16
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this.description = Description_in;
this.verion = "No Version is defined for this class";
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
protected String version;
public String Version
{
get
{
return this.version;
}
//if we ever want our attribute user to set this property,
//we must specify set method for it
set
{
this.verion = value;
}
}
}
[Help("This is Class1")]
public class Class1
{
}
[Help("This is Class2", Version = "1.0")]
public class Class2
{
}
[Help("This is Class3", Version = "2.0", Description = "This is do-nothing class")]
public class Class3
{
}
پس از اینکه این صفت را بر روی کلاس Class1 اعمال کردیم و بخواهیم آنرا کامپایل کنیم با پیغام زیر روبرو میشویم :
Help.Description : This is Class1
Help.Version :No Version is defined for this class
چون در اینجا هیچ مقداری برای Version در نظر نگرفتهایم، با این پیام مواجه شدهایم.
حال نتیجه اعمال این صفت را بر روی کلاس دوم بررسی میکنیم.
Help.Description : This is Class2
Help.Version : 1.0
برای پارامترهای اختیاری معمولا از دو سازنده استفاده نمیشود و در عوض از پارامترهای Named استفاده میگردد. نکتهای که باید به آن توجه کنید آنست که برای پارامترهای Named حتما باید در تعریف property، از متد set نیز استفاده نمایید در غیر اینصورت با پیغام خطای زیر روبرو میشوید :
'Version' : Named attribute argument can't be a read only property
بنابراین درصورتیکه این صفت را بر روی کلاس سوم نیز اعمال کنیم با پیغام خطای مشابهی روبرو خواهیم شد. اگر در کلاس Help تغییری کوچکی اییجاد کنیم و به Description نیز متد set را بیفزاییم، با خطا مواجه نخواهیم شد.
Help.Description : This is do-nothing class
Help.Version : 2.0
اتفاقی که در اینجا رخ میدهد آنست که در ابتدا سازنده (Constructor) این صفت به همراه پارامترهای Positional آن فراخوانده میشوند و سپس متد set برای هر یک از پارامترهای Named فراخوانده میشود .
انواع (type) معتبر برای پارامترهای صفت
انواع معتبر برای پارامترهای صفت بشرح زیر میباشند :
bool,byte,char,double,float,int,long,short,string,System.Type ,object
همچنین میتوان از enum و یا آرایهای تک بعدی، که عناصر آن یکی از انواع فوق باشد، نیز استفاده نمود.
استفاده از صفتها در زمان اجرا
تا کنون با طریقه ساخت صفتها و چگونگی استفاده و اعمال آنها بر عناصر مختلف برنامه آشنا شدیم. حال نوبت به آن رسیده است تا ببینیم چگونه میتوان از صفتها در زمان اجرا استفاده نمود. برای جستجوی (query) یک برنامه درباره صفت موجود در آن، به Reflection نیازمندیم. Reflection قابلیت بدست آوردن اطلاعات مربوط به انواع (Types) مختلف در زمان اجرای برنامه است. با استفاده از توابع Reflection موجود در .Net Framework میتوانیم با جستجو و پیمایش Metadate مربوط به یک اسمبلی، لیست کاملی از کلاسها، انواع و متدهایی را که برای آن اسمبلی خاص تعریف شدهاند را، بدست آوریم. به مثال 9-16 در این باره توجه نمایید.
مثال 9-16 : استفاده از Reflection
using System;
using System.Reflection;
using System.Diagnostics;
//attaching Help attribute to entire assembly
[assembly : Help("This Assembly demonstrates custom attributes creation and their run-time query.")]
//our custom attribute class
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
//
// TODO: Add constructor logic here
this.description = Description_in;
//
}
protected String description;
public String Description
{
get
{
return this.deescription;
}
}
}
//attaching Help attribute to our AnyClass
[HelpString("This is a do-nothing Class.")]
public class AnyClass
{
//attaching Help attribute to our AnyMethod
[Help("This is a do-nothing Method.")]
public void AnyMethod()
{
}
//attaching Help attribute to our AnyInt Field
[Help("This is any Integer.")]
public int AnyInt;
}
class QueryApp
{
public static void Main()
{
}
}
مبحث صفتها بسیار گسترده است و میتوان ساعتها در مورد صفتهای مختلف بحث نمود. اما آنچه مسلم است تمرین مستمر و پیگیری برنامه نویس در یافتن مواردی که میتوان با استفاده از صفتها، برنامهای پویاتر ایجاد نمود، مهمترین عامل در فهم و درک کامل مبحث خواهد بود. درباره Reflection نیز در آینده در یان سایت مفصلا توضیح خواهم داد.
خلاصه :
در این درس با صفتها آشنا شدید. یاد گرفتید که چگونه از صفتهای موجود در .Net Framework استفاده کرده و همچنین چگونه صفتهای شخصی و دلخواه خود را تولید نمایید. همچنین با پارامترهای صفتها و انواع آنها و نیز، عناصری که صفتها بر روی آنها اعمال میشوند آشنا شدید. در انتهای درس نیز به مختصر درباره Reflection و چگونگی استفاده از صفتها در زمان اجرا صحبت کردیم. امیدوارم مفید واقع شده باشد.