صفات و استفاده از آنها
در اين درس با نحوه استفاده از صفتها در زبان C# آشنا خواهيد شد. اهداف ما در اين درس به شرح زير است :
تعريف و يا کنترل موارد استفاده از يک صفت
استفاده از پارامترهای Positional و Named در صفتهای شخصی
انواع (type) معتبر برای پارامترهای صفت
پس از اينکه با طريقه استفاده از صفتهای موجود در زبان آشنا شديد، حال نوبت به ساخت صفتهای شخصی ميرسد. برای توليد يک صفت (Attribute) بايد يک کلاس ايجاد نماييم و اين کلاس بايد از System.Attribute مشتق شود. کلاسی که از System.Attribute مشتق ميشود (چه بطور مستقيم و چه بطور غير مستقيم) يک کلاس صفت(Attribute Class) است. اعلان کلاس صفت باعث ايجاد صفت جديدی ميشود که ميتوان از آن در برنامه استفاده نمود. به مثال 5-16 توجه فرماييد.
using System;
public class HelpAttribute : Attribute
{}
در اين مثال به سادگی يک صفت جديد توليد کردهايم و ميتوانيم از آن استفاده کنيم.
[Help()]
public class AnyClass
{}
همانطور که قبلا نيز گفتيم استفاده از کلمه Attribute به دنبال نام صفت الزامی نيست. صفتی که در اينجا ايجاد کردهايم عملا کار خاصی برای ما انجام نميدهد پس اندکی در کد آن تغيير ايجاد ميکنيم تا مفيدتر باشد.
using System;
public class HelpAttribute : Attribute
{public HelpAttribute(String Descrition_in)
{this.description = Description_in;
}
protected String description;
public String Description
{get
{return this.description;
}
}
}
[Help("this is a do-nothing class")]public class AnyClass
{}
هماطور که مشاهده ميکنيد با اضافه کردن چند خط کد توانستيم اين صفت را کاراتر کنيم. با قرار دادن يک property در اين صفت، پارامتر اين صفت بعنوان پيغام نمايش داده ميشود.
تعريف و يا کنترل موارد استفاده از يک صفت
AttributeUsage يکی از کلاسهای از پيش تعريف شده در زبان است که با استفاده از آن ميتوانيم موارد استفاده از صفتی را که توليد کردهايم را کنترل کنيم.
اين کلاس دارای سه property مختلف است که ميتوان آنها را به هنگام استفاده صفت شخصی تنظيم نمود و مورد استفاده قرار داد.
ValidOn
با استفاده از اين property ميتوانيم مشخص کنيم که صفت توليد شده توسط ما، بر روی کدام يک از عناصر برنامه قابل اعمال هستند. اطلاعات اين عناصر از AttributeTarget گرفته ميشود و ميتوان عناصر مختلف را بوسيله OR بيتی با يکديگر ترکيب نمود.
AllowMultiple
با استفاده از اين property ميتوان مشخص کرد که آيا ميتوان از اين صفت بيش از يکبار بر روی يک عنصر برنامه استفاده کرد يا نه.
Inherited
با استفاده از اين property ميتوان قوانين ارثبری اين صفت را کنترل نمود. با استفاده از اين property ميتوان مشخص کرد که آيا کلاسی که از کلاسی که صفت بر روی آن اعمال شده، ارث بری ميکند نيز، صفت بر رويش اعمال ميشود يا نه و يا به عبارتی صفت در کلاس مشتق شده نيز مورد ارثبری قرار ميگيرد يا نه.
حال با استفاده از موارد گفته شده در بالا، ميخواهيم اين مطالب را بر روی صفتی که خودمان توليد کرديم اعمال نماييم. مثال 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")] //errorpublic void AnyMethod()
{}
}
و کد خطای توليد شده بشکل زير خواهد بود :
AnyClass.cs: Attribute
'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
توجه کنيد که با استفاده از AttributeTargets.All به صفت Help اين امکان را ميدهيم تا بر روی تمامی عناصر موجود اعمال شود. ليست کامل عناصر مجاز نيز بشرح زير است :
-
Assembly,
-
Module,
-
Class,
-
Struct,
-
Enum,
-
Constructor,
-
Method,
-
Property,
-
Field,
-
Event,
-
Interface,
-
Parameter,
-
Delegate,
-
All = Assembly , Module , Class , Struct , Enum , Constructor , Method , Property , Field , Event , Interface , Parameter , Delegate,
-
ClassMembers = Class , Struct , Enum , Constructor , Method , Property , Field , Event , Delegate , Interface
حال به 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")] //errorpublic 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 خود اضافه نماييم.
[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 و چگونگي استفاده از صفتها در زمان اجرا صحبت كرديم. اميدوارم مفيد واقع شده باشد
مهندسی نرم افزار کامپیوتر