آموزش کامپیوتر

درس شانزدهم – استفاده از صفتها در C#

در اين درس با نحوه استفاده از صفتها در زبان C# آشنا خواهيد شد. اهداف ما در اين درس به شرح زير است :

1-     صفتها چه هستند و چرا از آنها استفاده مي‌کنيم

2-     استفاده از صفتهای تک پارامتری و چند پارامتری

3-   انواع پارامترهاي صفت (پارامترهاي Named و Positional)

4- Target های صفتها (عناصری که صفتها بر روی آنها اعمال مي‌شوند)

5- توليد صفتهای شخصی

6- تعريف و يا کنترل موارد استفاده از يک صفت

7- استفاده از پارامترهای Positional و Named در صفتهای شخصی

8- انواع (type) معتبر برای پارامترهای صفت

9- استفاده از صفتها در زمان اجرا

10- خلاصه مطالب


 

صفتها در حقيقت اطلاعات توضيحی هستند که مي‌توانيد آنها را به برنامه‌های خود بيفزاييد. صفتها را مي‌توان برای کليه عناصر برنامه از قبيل کلاسها، واسطها، اسمبلی ها و ... مورد استفاده قرار داد. از اين اطلاعات مي‌توان برای موارد متنوعی در زمان اجرای برنامه استفاده نمود. برای مثال مي‌توان به صفتی مانند 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()

    ...

اين پارامتر، ويژگی خاصی را به صفت مي‌افزايد که آن را با دو اعلان قبلی متمايز مي‌نمايد. نتيجه هر سه اعلان اين صفت در زير آورده شده است. اين پيغامها، پيامهای کامپايلر C# هستند که به هنگام کامپايل برنامه توليد شده‌اند.

 

>csc BasicAttributeDemo.cs
Microsoft (R) Visual C# .NET Compiler version 7.10.2292.4
for Microsoft (R) .NET Framework version 1.1.4322
Copyright (C) Microsoft Corporation 2001-2002. All rights reserved.
 
BasicAttributeDemo.cs(29,3): warning CS0612:
'BasicAttributeDemo.MyFirstDeprecatedMethod()' is obsolete
BasicAttributeDemo.cs(30,3): warning CS0612:
'BasicAttributeDemo.MySecondDeprecatedMethod()' is obsolete
BasicAttributeDemo.cs(31,3): warning CS0618:
'BasicAttributeDemo.MyThirdDeprecatedMethod()' is obsolete: 'You shouldn't use this method anymore.'

 

همانطور که ملاحظه مي‌کنيد، سومين اعلان صفت در اين برنامه که با پارامتر همراه بود، باعث شده است تا پارامتر صفت نيز به عنوان بخشی از پيام نمايش داده شده توسط کامپايلر، نشان داده شود. در مورد دو صفت ديگر نيز مشاهده مي‌شود که تنها پيغامی ساده توليد گرديده است.

 

مثال 1-16 شامل صفت ديگری نيز مي‌باشد. اين صفت STAThreadAttribute است که معمولا در ابتدای کليه برنامه‌های C# و قبل از آغاز متد Main() قرار مي‌گيرد. اين صفت بيان مي‌دارد که برنامه C# مورد نظر مي‌تواند با کد مديريت نشده COM از طريق Simple Threading Apartment ارتباط برقرار نمايد. استفاده از اين صفت در هر برنامه‌ای مي‌تواند مفيد باشد، چراکه شما بعنوان برنامه نويس هيچ‌گاه اطلاع نداريد که آيا کنابخانه ثالثی که از آن استفاده مي‌کنيد، قصد برقراری ارتباط با COM را دارد يا نه؟ (در صورتيکه با برخی از اصطلاحات بکار رفته آشنايي نداريد اصلا نگران نشويد. در اينجا هدف تنها نشان دادن موارد استفاده از صفتهاست.)

 

صفتها مي‌توانند دارای چندين پارامتر باشند. در مثال 2-16، استفاده از دو پارامتر برای يک صفت نشان داده شده است.

 

مثال 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)

همانطور که در بالا نيز اشاره شد، صفتها مي‌توانند دارای پارامتر نيز باشند. اين پارامترها به دو دسته تقسيم مي‌شوند. پارامترهای محلی (positional) و پارامترهای اسمی (named). از پارامترهای positional در زمانی استفاده مي‌شود که مي‌خواهيم پارامتر مورد نظر بصورت اجباری مورد استفاده قرار گيرد و البته اين مسئله يک قانون نيست ! چراکه در مورد صفت Obsolete، اين صفت دارای يک پارامتر positional ديگر با نام error و از نوع int نيز مي‌باشد که ما آنرا در مثال 1-16 لحاظ نکرديم. همانطور که در مثال 2-16 مشاهده کرديد، از اين پارامتر positional مي‌توان برای ايجاد يک خطا در زمان کامپايل برنامه استفاده نمود.

 
[Obsolete("Don't use Old method, use New method", true)]
static void Old( ) { }

 

تفاوت پارامترهای positional با پارامترهای named در آنست که، پارامترهای named با نامشان مورد استفاده قرار مي‌گيرند و هميشه اختياری هستند. در مثال 3-16 صفت DllImport را مشاهده مي‌نماييد که دارای هر دو نوع پارامتر positional و named است.

 

مثال 3-16

 

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 نحوه استفاده از اين صفت نشان داده شده است.

 

مثال 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 استفاده نماييم که اين قابليت بزرگی را در اختيار ما قرار خواهد داد.

 

توليد صفتهای شخصی

پس از اينکه با طريقه استفاده از صفتهای موجود در زبان آشنا شديد، حال نوبت به ساخت صفتهای شخصی مي‌رسد. برای توليد يک صفت (Attribute) بايد يک کلاس ايجاد نماييم و اين کلاس بايد از System.Attribute مشتق شود. کلاسی که از System.Attribute مشتق مي‌شود (چه بطور مستقيم و چه بطور غير مستقيم) يک کلاس صفت(Attribute Class)  است. اعلان کلاس صفت باعث ايجاد صفت جديدی مي‌شود که مي‌توان از آن در برنامه استفاده نمود. به مثال 5-16 توجه فرماييد.

 

مثال 5-16

using System;
public class HelpAttribute : Attribute
{
}

در اين مثال به سادگی يک صفت جديد توليد کرده‌ايم و مي‌توانيم از آن استفاده کنيم.

 

[Help()]
public class AnyClass
{
}

 

همانطور که قبلا نيز گفتيم استفاده از کلمه Attribute به دنبال نام صفت الزامی نيست. صفتی که در اينجا ايجاد کرده‌ايم عملا کار خاصی برای ما انجام نمي‌دهد پس اندکی در کد آن تغيير ايجاد مي‌کنيم تا مفيدتر باشد.

 

مثال 6-16

 

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 را بررسی نماييد.

 

مثال 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 اين امکان را مي‌دهيم تا بر روی تمامی عناصر موجود اعمال شود. ليست کامل عناصر مجاز نيز بشرح زير است :

  • 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")]        //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 و چگونگي استفاده از صفتها در زمان اجرا صحبت كرديم. اميدوارم مفيد واقع شده باشد.

+ نوشته شده در  پانزدهم فروردین 1387ساعت 9:52  توسط طاهر  | 

 


طراحی سایت و بهینه سازی سایت توسط : گروه طراحان نگاه تازه