صفات و استفاده از آنها
در اين درس با نحوه استفاده از صفتها در زبان C# آشنا خواهيد شد. اهداف ما در اين درس به شرح زير است :
صفت ها چه هستند و چرا از آنها استفاده ميکنيم
استفاده از صفتهای تک پارامتری و چند پارامتری
انواع پارامترهاي صفت (پارامترهاي Named و Positional)
صفتها در حقيقت اطلاعات توضيحی هستند که ميتوانيد آنها را به برنامههای خود بيفزاييد. صفتها را ميتوان برای کليه عناصر برنامه از قبيل کلاسها، واسطها، اسمبلی ها و ... مورد استفاده قرار داد. از اين اطلاعات ميتوان برای موارد متنوعی در زمان اجرای برنامه استفاده نمود. برای مثال ميتوان به صفتی مانند DllImportAttribute اشاره کرد که امکان برقراری ارتباط با توابع کتابخانهای Win32 را فراهم مينمايد. همچنين صفتهايي نيز وجود دارند که برنامهنويس يا توسعه دهنده برنامه را در امر توليد برنامه ياری مينمايند. برای مثال ميتوان به صفت ObsoleteAttribute اشاره کرد که با استفاده از آن، در زمان کامپايل برنامه پيغامی برای برنامه نويس نمايش داده ميشود و مشخص ميکند که متدی خاص مورد استفاده قرار نگرفته و يا ديگر مورد استفاده نيست. همچنين هنگاميکه با فرمهای ويندوز کار ميکنيم، صفتهای بسياری وجود دارند که امکان استفاده از اين فرمها را فراهم کرده و باعث ميشوند تا اطلاعات مربوط به اين عناصر در property فرم ظاهر شوند. يکی ديگر از موارد استفاده از صفتها در مسايل امنيتی اسمبليهای .Net است. برای مثال صفتهايي وجود دارند که باعث جلوگيری از فراخوانيهای غير مجاز ميشوند، بدين معنی که تنها اجازه فراخوانی را به متدها يا اشيايي ميدهند که قبلا تعريف شده و مشخص شده باشند.
يکی از علتهای استفاده از صفتها آنست که، اغلب سرويسهايي را که آنها برای کاربر فراهم مينمايند، بسيار پيچيده است و با کدهای معمولی نميتوان آنرا را بدست آورد. از اينرو استفاده از صفتها در بسياری از موارد ضروری و اجتناب ناپذير است. همانطور که خواهيد ديد، صفتها به برنامههاي ما Metadata اضافه مينمايند. پس از کامپايل برنامههای C#، فايل اسمبلی برای آن ايجاد ميگردد که اين اسمبلی معمولا يا يک فايل اجرايي است و يا يک Dll است. توصيف اسمبلی، در Metadata ي مربوط به آن قرار ميگيرد. طی پروسهای تحت عنوان Reflection، صفت يک برنامه از طريق فايل Metadata ي موجود در اسمبلی آن قابل دسترس ميگردد. .(برای آشنايي بيشتر با اسمبلی و Metadata ميتوانيد به " کامپايل يک برنامه سی شارپ " در همين سايت مراجعه نماييد.) در حقيقت صفتها، کلاسهايي هستند که ميتوانيد آنها را با زبان C# توليد کرده و جهت افزودن اطلاعاتی توضيحی به کد خود، از آنها استفاده نماييد. اين اطلاعات در زمان اجرای برنامه از طريق Reflection قابل دسترسی هستند.
در اين درس با روش استفاده از صفتها و چگونگی ارتباط دادن آنها با عناصر مختلف برنامه آشنا خواهيد شد.
صفتها را معمولا قبل از اعلان عنصر مورد نظر در برنامه قرار ميدهند. اعلان صفتها بدين صورت است که نام صفت درون دو براکت قرار ميگيرد.
[ObsoleteAttribute]
استفاده از کلمه Attribute در اعلان صفت الزامی نيست، از اينرو اعلان زير با اعلان فوق يکسان است :
[Obsolete]
همچنين صفتها ميتوانند دارای پارامتر نيز باشند که با استفاده از آنها خواص بيشتری را در اختيار برنامه قرار ميدهند. در مثال 1-16 موارد متنوعی از استفاده صفت ObsoleteAttribute را مشاهده مينماييد.
مثال 1-16 : نحوه استفاده از صفتها
using System;
class BasicAttributeDemo
{
[Obsolete]
public void MyFirstDeprecatedMethod()
{
Console.WriteLine("Called MyFirstDeprecatedMethod().");
}
[ObsoleteAttribute]
public void MySecondDeprecatedMethod()
{
Console.WriteLine("Called MySecondDeprecatedMethod().");
}
[Obsolete("You shouldn't use this method anymore.")]
public void MyThirdDeprecatedMethod()
{
Console.WriteLine("Called MyThirdDeprecatedMethod().");
}
// make the program thread safe for COM
[STAThread]
static void Main(string[] args)
{
BasicAttributeDemo attrDemo = new BasicAttributeDemo();
attrDemo.MyFirstDeprecatedMethod();
attrDemo.MySecondDeprecatedMethod();
attrDemo.MyThirdDeprecatedMethod();
}
}
همانطور که در مثال 1-16 نيز مشاهده ميشود، صفت Obsolete در فرمهای مختلف مورد استفاده قرار گرفته است. اولين محلی که از اين صفت استفاده شده است، متد MyFirstDeprecatedMethod() و پس از آن در متد MySecondDeprecatedMethod() است. تنها تفاوت استفاده در اين دو حالت آنست که در متد دوم صفت با نام کامل يعنی به همراه کلمه Attribute مورد استفاده قرار گرفته است. نتيجه هر دو اعلان يکسان است. همانطور که گفته بوديم، صفتها ميتوانند دارای پارامتر نيز باشند :
[Obsolete("You shouldn't use this method anymore.")]
public void MyThirdDeprecatedMethod()
...
مثال 1-16 شامل صفت ديگری نيز ميباشد. اين صفت STAThreadAttribute است که معمولا در ابتدای کليه برنامههای C# و قبل از آغاز متد Main() قرار ميگيرد. اين صفت بيان ميدارد که برنامه C# مورد نظر ميتواند با کد مديريت نشده COM از طريق Simple Threading Apartment ارتباط برقرار نمايد. استفاده از اين صفت در هر برنامهای ميتواند مفيد باشد، چراکه شما بعنوان برنامه نويس هيچگاه اطلاع نداريد که آيا کنابخانه ثالثی که از آن استفاده ميکنيد، قصد برقراری ارتباط با COM را دارد يا نه؟ (در صورتيکه با برخی از اصطلاحات بکار رفته آشنايي نداريد اصلا نگران نشويد. در اينجا هدف تنها نشان دادن موارد استفاده از صفتهاست.)
صفتها ميتوانند دارای چندين پارامتر باشند. در مثال 2-16، استفاده از دو پارامتر برای يک صفت نشان داده شده است.
using System;
public class AnyClass
{ [Obsolete("Don't use Old method, use New method", true)] static void Old( ) { } static void New( ) { }public static void Main( )
{Old( );
}
}
همانطور که در مثال 2-16 مشاهده ميکنيد، صفت مورد استفاده دارای دو پارامتر است. پارامتر اول که يک جمله متنی است و همانند مثال 1-16 عمل ميکند. پارامتر دوم نيز بيان کننده نوع پيغامی است که اين صفت در هنگام کامپايل توليد ميکند. در صورتيکه اين مقدار برابر با True باشد، بدين معناست که در هنگام کامپايل پيغام خطا توليد ميشود و کامپايل برنامه متوقف ميگردد. در حالت پيش فرض مقدار اين پارامتر برابر با False است که بيان ميدارد، به هنگام کامپايل تنها پيغام هشداری توليد خواهد شد. در پيغام اين برنامه، عنصری از برنامه را که نبايد از آن استفاده شود معين شده و جايگزين آن نيز معرفی ميشود.
AnyClass.Old()' is obsolete:
'Don't use Old method, use New method'
تفاوت پارامترهای positional با پارامترهای named در آنست که، پارامترهای named با نامشان مورد استفاده قرار ميگيرند و هميشه اختياری هستند. در مثال 3-16 صفت DllImport را مشاهده مينماييد که دارای هر دو نوع پارامتر positional و named است.
using System;
using System.Runtime.InteropServices;
class AttributeParamsDemo
{
[DllImport("User32.dll", EntryPoint="MessageBox")]
static extern int MessageDialog(int hWnd, string msg, string caption, int msgType);
[STAThread]
static void Main(string[] args)
{
MessageDialog(0, "MessageDialog Called!", "DllImport Demo", 0);
}
}
صفت DllImport در مثال 3-16 دارای يک پارامتر positional ("User32.dll") و يک پارامتر named (EntryPoint="MessageBox") است . پارامترهای named در هر مکانی ميتوانند قرار گيرند و مانند پارامترهای positional دارای محدوديت مکانی نيستند. بدين معنا که چون در پارامترهای named، نام پارامتر مستقيما مورد استفاده قرار ميگيرد، محل قرار گيری آن در ليست پارامترهای صفت مهم نيست اما در مورد پارامترهای positional چون اسم پارامتر مورد استفاده قرار نميگيرد، اين پارامترها حتما بايد در مکانهای تعيين شده و تعريف شده در ليست پارامترهای صفت قرار گيرند. توجه کنيد که چون هدف ما تنها آشنايي با صفتها و نحوه استفاده از آنهاست، درباره پارامترهای مختلف صفت DllImport بحث نخواهيم کرد چراکه پارامترهای اين صفت نياز به آشنايي کامل با Win32 API دارد.
در يک بررسی کلی ميتوان گفت که پارامترهای Positional، پارامترهای سازنده(Constructor) صفت هستند و در هر بار استفاده از صفت بايد مورد استفاده قرار گيرند، ولی پارامترهای Named کاملا اختياری هستند و هميشه نيازی به استفاده از آنها نميباشد.
Target های صفتها (عناصری که صفتها بر روی آنها اعمال ميشوند)
صفتهايي که تا کنون مشاهده کرديد، همگی بر روی متدها اعمال شده بودند. اما عناصر مختلف ديگری در C# وجود دارند که ميتوان صفتها را بر روی آنها اعمال نمود. جدول 1-16 عناصر مختلف زبان C# را که صفتها بر روی آنها اعمال ميشوند را نشان ميدهد.
|
قابل اعمال به .... |
عناصر اعمال شونده |
|
به تمامی عناصر قابل اعمال هستند. |
all |
|
به تمام يک اسمبلی |
assembly |
|
کلاسها |
class |
|
سازندهها |
constructor |
|
Delegate ها |
delegates |
|
عناصر شمارشی |
enum |
|
رخدادها |
event |
|
فيلدها |
field |
|
واسطها |
interface |
|
متدها |
method |
|
ماژولها (کدهای کامپايل شدهای که ميتوانند به عنوان قسمتی از يک اسمبلی در نظر گرفته شوند.) |
module |
|
پارامترها |
parameter |
|
Property ها |
property |
|
مقادير بازگشتی |
returnvalue |
|
ساختارها |
struc |
هر چند ممکن است استفاده از اين Target ها باعث ايجاد ابهام شوند، اما ميتوان با استفاده از اين Target ها معين کرد که صفت دقيقا به عنصر مورد نظر اعمال شود. يکی از صفتهايي که بر روی اسمبلی اعمال ميشود و باعث ارتباط با CLS ميگردد، صفت CLSCompliantAttribute است. CLS يا همان Common Language Specification امکان برقراری ارتباط بين کليه زبانهايي که تحت .Net کار ميکنند را فراهم مينمايد. Target های صفتها با استفاده از اسم Target که بعد از آن کولون قرار ميگيرد، ايجاد ميشوند. در مثال 4-16 نحوه استفاده از اين صفت نشان داده شده است.
using System;
[assembly:CLSCompliant(true)]
public class AttributeTargetDemo
{
public void NonClsCompliantMethod(uint nclsParam)
{
Console.WriteLine("Called NonClsCompliantMethod().");
}
[STAThread]
static void Main(string[] args)
{
uint myUint = 0;
AttributeTargetDemo tgtDemo = new AttributeTargetDemo();
tgtDemo.NonClsCompliantMethod(myUint);
}
}
با استفاده از Target مورد نظر در اينجا يعنی assembly، اين صفت بر روی کل اسمبلی اعمال ميگردد. کد موجود در مثال 4-16 کامپايل نخواهد شد، زيرا uint در متد NonClsCompliantMethod() اعلان شده است. در اينجا درصورتيکه فرم پارامتر صفت CLSCompliant را به false تغيير دهيد و يا متد NonClsCompliantMethod() را به متدی منطبق با CLS تبديل کنيد (مثلا نوع بازگشتی آنرا int تعريف کنيد) آنگاه برنامه کامپايل خواهد شد. (توضيحي كه درباره CLS ميتوانم بيان كنم اينست كه CLS مجموعهاي از ويژگيها و خواص .Net Framework است كه به نحوي بيان ميدارد، براي اينكه زبانهاي مختلف تحت .Net بتوانند بدون مشكل با يكديگر ارتباط برقرار نمايند، لازم است از يك سري از قوانين پيروي كنند، در غير اينصورت امكان برقراري ارتباط با ساير كدهاي نوسته شده تحت زبانهاي برنامهسازي ديگر را نخواهند داشت. براي مثال، استفاده از نوع uint به دليل اينكه در زبانهاي مختلف ميتواند به صورتهاي متفاوتي پيادهسازي شود و يا وجود نداشته باشد، سازگار با CLS نيست و براي اينكه بخواهيم برنامهاي منطبق با CLS داشته باشيم نبايد از آن استفاده نماييم.)
نکته قابل توجه در مورد مثال 4-16 آنست که در اين مثال صفت CLSCompliant به استفاده از يک Target که همان assembly است، مورد استفاده قرار گرفته است و از اينرو تمامی مشخصات اين صفت به کليه اعضای اين اسمبلی اعمال خواهند شد. توجه نماييد که در اين مثال علت و موارد استفاده از صفتها مشهودتر است، چراکه همانطور که مشاهده مينماييد، با استفاده از يک صفت ميتوانيم کنترلی بر روی کل اسمبلی و برنامه قرار دهيم تا در صورتيکه ميخواهيم برنامه ما با ساير زبانهای برنامهسازی تحت .Net ارتباط برقرار کند، از متدهای استاندارد و سازگار با CLS استفاده نماييم که اين قابليت بزرگی را در اختيار ما قرار خواهد داد.
مهندسی نرم افزار کامپیوتر