سه شنبه ۲۷ شهريور ۱۴۰۳
Tut24 آموزش برنامه نویسی و مجله تخصصی فناوری ورود/عضویت

آموزش سی شارپ - ویژگی‌ها

یک ویژگی یک برچسب تعریفی است که برای انتقال اطلاعات به زمان اجرا درباره رفتارهای عناصر مختلف مانند کلاس‌ها، متدها، ساختارها، شمارشی‌ها و اسمبلی‌ها در برنامه شما استفاده می‌شود. شما می‌توانید با استفاده از یک ویژگی، اطلاعات تعریفی را به یک برنامه اضافه کنید. یک برچسب تعریفی با استفاده از براکت‌های مربعی ([ ]) بالای عنصری که برای آن استفاده می‌شود، نمایش داده می‌شود.

ویژگی‌ها برای اضافه کردن فراداده‌ها مانند دستور کامپایلر و اطلاعات دیگر مانند نظرات، توضیحات، متدها و کلاس‌ها به یک برنامه استفاده می‌شوند. چارچوب .Net دو نوع ویژگی را ارائه می‌دهد: ویژگی‌های پیش‌تعیین شده (Pre-defined Attributes) و ویژگی‌های سفارشی ساخته شده (Custom Built Attributes).

مشخص کردن یک ویژگی

نحوی برای مشخص کردن یک ویژگی به شرح زیر است:

[attribute(positional_parameters, name_parameter = value, ...)]
element

نام ویژگی و مقادیر آن در داخل براکت‌های مربعی مشخص می‌شود، قبل از عنصری که ویژگی به آن اعمال می‌شود. پارامترهای موقعیتی اطلاعات ضروری را مشخص می‌کنند و پارامترهای نام‌گذاری شده اطلاعات اختیاری را مشخص می‌کنند.

ویژگی‌های پیش‌تعیین شده (Predefined Attributes)

چارچوب .Net سه ویژگی پیش‌تعیین شده را فراهم می‌کند:

  • AttributeUsage
  • Conditional
  • Obsolete

AttributeUsage

ویژگی پیش‌تعیین شده AttributeUsage شرح می‌دهد که چگونه یک کلاس ویژگی سفارشی استفاده می‌شود. این ویژگی نوع عناصری که ویژگی می‌تواند بر روی آنها اعمال شود را مشخص می‌کند.

نحوی برای مشخص کردن این ویژگی به شرح زیر است:

[AttributeUsage (
   validon,
   AllowMultiple = allowmultiple,
   Inherited = inherited
)]

که در آن،

  • پارامتر validon عناصر زبانی را مشخص می‌کند که ویژگی می‌تواند بر روی آن‌ها قرار گیرد. این یک ترکیب از مقدار شمارنده AttributeTargets است. مقدار پیش‌فرض AttributeTargets.All است.

  • پارامتر allowmultiple (اختیاری) مقداری برای خاصیت AllowMultiple این ویژگی فراهم می‌کند، یک مقدار بولین. اگر این مقدار true باشد، ویژگی چندباره استفاده می‌شود. مقدار پیش‌فرض false است (استفاده یک‌باره).

  • پارامتر inherited (اختیاری) مقداری برای خاصیت Inherited این ویژگی فراهم می‌کند، یک مقدار بولین. اگر true باشد، ویژگی توسط کلاس‌های مشتق شده به ارث می‌برد. مقدار پیش‌فرض false است (غیر موروثی).

به عنوان مثال،

[AttributeUsage(
   AttributeTargets.Class |
   AttributeTargets.Constructor |
   AttributeTargets.Field |
   AttributeTargets.Method |
   AttributeTargets.Property, 
   AllowMultiple = true)]

Conditional

این ویژگی پیش‌تعیین شده یک متد شرطی را نشان می‌دهد که اجرای آن بستگی به یک شناسه پیش‌پردازش مشخص دارد.

این ویژگی منجر به کامپایل شرطی تماس‌های متد می‌شود، بسته به مقدار مشخص شده مانند Debug یا Trace. به عنوان مثال، مقادیر متغیرها را در هنگام اشکال‌زدایی کد نمایش می‌دهد.

نحوی برای مشخص کردن این ویژگی به شرح زیر است:

[Conditional(
   conditionalSymbol
)]

مثلا،

[Conditional("DEBUG")]

مثال زیر ویژگی − را نشان می دهد

#define DEBUG
using System;
using System.Diagnostics;

public class Myclass {
   [Conditional("DEBUG")]
   
   public static void Message(string msg) {
      Console.WriteLine(msg);
   }
}
class Test {
   static void function1() {
      Myclass.Message("In Function 1.");
      function2();
   }
   static void function2() {
      Myclass.Message("In Function 2.");
   }
   public static void Main() {
      Myclass.Message("In Main function.");
      function1();
      Console.ReadKey();
   }
}

هنگامی که کد بالا کامپایل و اجرا می شود، نتیجه زیر را ایجاد می کند -

In Main function
In Function 1
In Function 2

Obsolete

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

نحوی برای مشخص کردن این ویژگی به شرح زیر است:

[Obsolete (
   message
)]

[Obsolete (
   message,
   iserror
)]

که در آن،

  • پارامتر message، یک رشته است که دلیل منسوخ شدن عنصر و چه چیزی باید به جای آن استفاده شود را توصیف می‌کند.

  • پارامتر iserror، یک مقدار بولین است. اگر مقدار آن true باشد، کامپایلر باید استفاده از عنصر را به عنوان یک خطا در نظر بگیرد. مقدار پیش‌فرض false است (کامپایلر هشداری را تولید می‌کند).

برنامه زیر این را نشان می‌دهد:

using System;

public class MyClass {
   [Obsolete("Don't use OldMethod, use NewMethod instead", true)]
   
   static void OldMethod() {
      Console.WriteLine("It is the old method");
   }
   static void NewMethod() {
      Console.WriteLine("It is the new method"); 
   }
   public static void Main() {
      OldMethod();
   }
}

وقتی می خواهید برنامه را کامپایل کنید، کامپایلر یک پیغام خطایی می دهد که می گوید -

 Don't use OldMethod, use NewMethod instead

ایجاد ویژگی‌های سفارشی

چارچوب .Net امکان ایجاد ویژگی‌های سفارشی را فراهم می‌کند که می‌توان از آن‌ها برای ذخیره اطلاعات تعریفی استفاده کرده و در زمان اجرا بازیابی کرد. این اطلاعات ممکن است مربوط به هر عنصر هدف باشد، بسته به معیارهای طراحی و نیاز برنامه.

ایجاد و استفاده از ویژگی‌های سفارشی شامل چهار مرحله است:

  • تعریف یک ویژگی سفارشی
  • ساخت ویژگی سفارشی
  • اعمال ویژگی سفارشی بر روی یک عنصر برنامه هدف
  • دسترسی به ویژگی‌ها از طریق بازتابی (Reflection)

مرحله آخر شامل نوشتن یک برنامه ساده برای خواندن متادیتا و یافتن نشانه‌های مختلف است. متادیتا اطلاعاتی درباره داده‌ها است یا اطلاعاتی که برای توصیف داده‌های دیگر استفاده می‌شود. این برنامه باید با استفاده از بازتابی (Reflection) بتواند به ویژگی‌ها در زمان اجرا دسترسی داشته باشد. در فصل بعد به این مورد خواهیم پرداخت.

تعریف یک ویژگی سفارشی

یک ویژگی سفارشی جدید باید از کلاس System.Attribute ارث‌بری شود. به عنوان مثال،

//a custom attribute BugFix to be assigned to a class and its members
[AttributeUsage(
   AttributeTargets.Class |
   AttributeTargets.Constructor |
   AttributeTargets.Field |
   AttributeTargets.Method |
   AttributeTargets.Property,
   AllowMultiple = true)]

public class DeBugInfo : System.Attribute

در کد قبلی، یک ویژگی سفارشی به نام DeBugInfo تعریف کرده‌ایم.

ساخت ویژگی سفارشی

بیایید یک ویژگی سفارشی به نام DeBugInfo بسازیم که اطلاعاتی که به وسیله اشکال‌زدایی هر برنامه به دست می‌آید را ذخیره کند. بگذارید این اطلاعات را شامل موارد زیر کنیم −

  • شماره کد برای خطا
  • نام توسعه‌دهنده‌ای که خطا را شناسایی کرده است
  • تاریخ آخرین مرور کد
  • یک پیام رشته‌ای برای ذخیره نظرات توسعه‌دهنده

کلاس DeBugInfo سه خاصیت خصوصی برای ذخیره اطلاعات اولیه و یک خاصیت عمومی برای ذخیره پیام دارد. بنابراین، شماره خطا، نام توسعه‌دهنده و تاریخ مرور به عنوان پارامترهای موقعیتی کلاس DeBugInfo در نظر گرفته می‌شوند و پیام پارامتر اختیاری یا با نام است.

هر ویژگی باید حداقل یک سازنده داشته باشد. پارامترهای موقعیتی باید از طریق سازنده منتقل شوند. کد زیر کلاس DeBugInfo را نشان می‌دهد −

//a custom attribute BugFix to be assigned to a class and its members
[AttributeUsage(
   AttributeTargets.Class |
   AttributeTargets.Constructor |
   AttributeTargets.Field |
   AttributeTargets.Method |
   AttributeTargets.Property,
   AllowMultiple = true)]

public class DeBugInfo : System.Attribute {
   private int bugNo;
   private string developer;
   private string lastReview;
   public string message;
   
   public DeBugInfo(int bg, string dev, string d) {
      this.bugNo = bg;
      this.developer = dev;
      this.lastReview = d;
   }
   public int BugNo {
      get {
         return bugNo;
      }
   }
   public string Developer {
      get {
         return developer;
      }
   }
   public string LastReview {
      get {
         return lastReview;
      }
   }
   public string Message {
      get {
         return message;
      }
      set {
         message = value;
      }
   }
}

اعمال ویژگی سفارشی

ویژگی با قرار دادن آن بلافاصله قبل از هدف آن اعمال می‌شود −

[DeBugInfo(45, "Sara", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Smith", "10/10/2012", Message = "Unused variable")]
class Rectangle {
   //member variables
   protected double length;
   protected double width;
   public Rectangle(double l, double w) {
      length = l;
      width = w;
   }
   [DeBugInfo(55, "Sara", "19/10/2012", Message = "Return type mismatch")]
   
   public double GetArea() {
      return length * width;
   }
   [DeBugInfo(56, "Sara", "19/10/2012")]
   
   public void Display() {
      Console.WriteLine("Length: {0}", length);
      Console.WriteLine("Width: {0}", width);
      Console.WriteLine("Area: {0}", GetArea());
   }
}

در درس بعدی، با استفاده از یک شیء کلاس Reflection، اطلاعات ویژگی را بازیابی می‌کنیم.