خواص (Properties)

property (خصوصیت) استانداردی در سی شارپ، برای دسترسی به اعضای داده‌ای (فیلدها) با سطح دسترسی private در داخل یک کلاس می‌باشد. همانطور که در درس قبل اشاره شد، تعریف فیلدها در داخل کلاس به صورت public اشتباه است، چون کاربران می‌توانند با ایجاد یک شیء از کلاس به آنها دسترسی داشته باشند و هر مقداری که دوست دارند به آنها اختصاص دهند. برای رفع این مشکل مفهوم property ارائه شد. هر property دارای دو بخش می‌باشد، یک بخش جهت مقدار دهی (بلوک set) و یک بخش برای دسترسی به مقدار (بلوک get) یک داده private می‌باشد. property ها باید به صورت public تعریف شوند تا در کلاسهای دیگر نیز قابل دسترسی می‌باشند. در مثال زیر نحوه تعریف و استفاده از property آمده است :

  1: using System;
  2: 
  3: namespace PropertiesDemo
  4: {
  5:     public class Person
  6:     {
  7:         private string name;
  8:         private int age;
  9:         private double height;
 10: 
 11:         public string Name       
 12:         {                        
 13:             get                  
 14:             {                    
 15:                 return name;     
 16:             }                    
 17:             set                  
 18:             {                    
 19:                 name = value;    
 20:             }                    
 21:         }                        
 22: 
 23:         public int Age           
 24:         {                        
 25:             get                  
 26:             {                    
 27:                 return age;      
 28:             }                    
 29:             set                  
 30:             {                    
 31:                 age = value;     
 32:             }                    
 33:         }                        
 34: 
 35:         public double Height     
 36:         {                        
 37:             get                  
 38:             {                    
 39:                 return height;   
 40:             }                    
 41:             set                  
 42:             {                    
 43:                 height = value;  
 44:             }                    
 45:         }                        
 46: 
 47:         public Person(string name, int age, double height)
 48:         {
 49:             this.name = name;
 50:             this.age = age;
 51:             this.height = height;
 52:         }
 53: 
 54:     }
 55: 
 56:     public class Program
 57:     {
 58:         public static void Main()
 59:         {
 60:             Person person1 = new Person("Jack", 21, 160);
 61:             Person person2 = new Person("Mike", 23, 158);
 62: 
 63:             Console.WriteLine("Name: {0}", person1.Name);
 64:             Console.WriteLine("Age: {0} years old", person1.Age);
 65:             Console.WriteLine("Height: {0}cm", person1.Height);
 66: 
 67:             Console.WriteLine(); //Seperator                     
 68: 
 69:             Console.WriteLine("Name: {0}", person2.Name);
 70:             Console.WriteLine("Age: {0} years old", person2.Age);
 71:             Console.WriteLine("Height: {0}cm", person2.Height);
 72: 
 73:             person1.Name = "Frank"; 
 74:             person1.Age = 19;       
 75:             person1.Height = 162;   
 76: 
 77:             person2.Name = "Ronald";
 78:             person2.Age = 25;       
 79:             person2.Height = 174;   
 80: 
 81:             Console.WriteLine(); //Seperator                     
 82: 
 83:             Console.WriteLine("Name: {0}", person1.Name);
 84:             Console.WriteLine("Age: {0} years old", person1.Age);
 85:             Console.WriteLine("Height: {0}cm", person1.Height);
 86: 
 87:             Console.WriteLine();
 88: 
 89:             Console.WriteLine("Name: {0}", person2.Name);
 90:             Console.WriteLine("Age: {0} years old", person2.Age);
 91:             Console.WriteLine("Height: {0}cm", person2.Height);
 92:         }
 93:     }
 94: }
Name: Jack
Age: 21 years old
Height: 160cm

Name: Mike
Age: 23 years old
Height: 158cm

Name: Frank
Age: 19 years old
Height: 162cm

Name: Ronald
Age: 25 years old
Height: 174cm

در برنامه بالا نحوه استفاده از property آمده است. همانطور که مشاهده می‌کنید در این برنامه ما سه فیلد (خطوط 9-7) تعریف کرده‌ایم (سه فیلد با سطح دسترسی private).

private string name;
private int age;
private double height;

دسترسی به مقادیر این فیلدها فقط از طریق property های ارائه شده (خطوط 45-11) امکان پذیر است.

public string Name
{
    get
    {
        return name;
    }
    set
    {
        name = value;
    }
}

public int Age
{
    get
    {
        return age;
    }
    set
    {
        age = value;
    }
}

public double Height
{
    get
    {
        return height;
    }
    set
    {
        height = value;
    }
}

وقتی یک خاصیت ایجاد می‌کنیم، باید سطح دسترسی آن را public تعریف کرده و نوع داده‌ای را که بر می‌گرداند یا قبول می‌کند را مشخص کنیم. به این نکته توجه کنید که نام property ها همانند نام فیلدهای مربوطه می‌باشد با این تفاوت که حرف اول آنها بزرگ نوشته می‌شود. البته یادآور می‌شویم که شباهت نام property ها و فیلدها اجبار نیست و یک قرارداد در سی شارپ می‌باشد.

به کلمه کلیدی value در داخل بلوک set توجه کنید. Value همان مقداری است که از طریق property به فیلد اختصاص می‌دهیم. برای اختصاص یک مقدار به یک فیلد از طریق property کافیست که به صورت زیر عمل کنید :

Object.Property = Value;

این کار (قرار دادن یک مقدار بعد از علامت مساوی) به منزله فراخوانی بخش set است. و ما به برنامه می‌فهمانیم که می‌خواهیم از طریق بخش set یک فیلد را مقداردهی کنیم. Object شیء ایجاد شده از کلاس، Property نام پراپرتی و Value مقداری است که می‌خواهیم به فیلد اختصاص دهیم. برای دسترسی به یک خاصیت می‌توانید از علامت دات (.) استفاده کنید. مثلاً برای اختصاص مقدار به سه فیلد age، name و height از طریق property باید به صورت زیر عمل کنید :

person1.Name = "Frank";
person1.Age = 19;
person1.Height = 162;

دستورات بالا بخش set مربوط به هر property را فراخوانی کرده و مقادیری به هر یک از فیلدها اختصاص می‌دهد. برای فراخوانی بخش get کافیست که نام شیء و سپس علامت نقطه و در آخر نام property را بنویسیم. با این کار به برنامه می‌فهمانیم که ما نیاز به مقدار فیلد داریم.

Console.WriteLine("Name: {0}", person1.Name);
Console.WriteLine("Age: {0} years old", person1.Age);
Console.WriteLine("Height: {0}cm", person1.Height));

به این نکته توجه کنید که در بخش get هم می‌توان تغییراتی بر روی فیلدها اعمال کرد. مثلاً فرض کنید که یک فیلد دارید که مقادیر پولی را در خود ذخیره می‌کند. شما می‌توانید در بخش get نحوه نمایش مقدار موجود در این فیلد را مشخص کنید. مثلاً خروجی به صورت سه رقم سه رقم نمایش داده شود. استفاده از property ها کد نویسی را انعطاف پذیر می‌کند مخصوصاً اگر بخواهید یک اعتبارسنجی برای اختصاص یک مقدار به فیلدها یا استخراج یک مقدار از آنها ایجاد کنید. پس می‌توان گفت که :

مثلاً شما می‌توانید یک محدودیت ایجاد کنید که فقط اعداد مثبت به فیلد age (سن) اختصاص داده شود. همانطور که در کد ابتدای درس مشاهده می‌کنید ما نوع فیلد age را int قرار داده‌ایم. یعنی کاربر می‌تواند هر رقمی بین اعداد 2147483648- تا 2147483647 را به این فیلد اختصاص دهد. ولی چون غیر معقولانه است و سن (age) باید یک عدد مثبت و از لحاظ عقلی عددی از 1 تا 100 باشد می‌توانیم کاربر را با استفاده از بخش set مجبور کنیم که رقمی بین این دو عدد را به age اختصاص دهد. می‌توانید با تغییر بخش set خاصیت Age این کار را انجام دهید :

  1: public int Age           
  2: {                        
  3:     get                  
  4:     {                    
  5:         return age;      
  6:     }                    
  7:     set                  
  8:     {                    
  9:         if (value > 0 && value <= 100)
 10:         age = value;                  
 11:         else                          
 12:         age = 0;                      
 13:     }                    
 14: }

حال اگر کاربر بخواهد یک مقدار منفی به فیلد age اختصاص دهد مقدار age صفر خواهد شد. همچنین می‌توان یک property فقط خواندنی (read-only) ایجاد کرد. این property فاقد بخش set است. به عنوان مثال می‌توان یک خاصیت Name فقط خواندنی مانند زیر ایجاد کرد :

public string Name
{
    get
    {
        return name;
    }
}

در این مورد اگر بخواهید یک مقدار جدید به فیلد name اختصاص دهید با خطا مواجه می‌شوید. نکته دیگری که باید به آن توجه کنید این است که شما می‌توانید برای بخش set یا get سطح دسترسی ایجاد کنید. به تکه کد زیر توجه کنید :

public string Name
{
    get
    {
        return name;
    }
    private set
    {
        name = value;
    }
}

خاصیت Name فقط در خارج از کلاس قابل خواندن است اما متدها فقط داخل کلاس Person می‌توانند مقادیر جدید بگیرند. یک property می‌تواند دارای دو فیلد باشد. به کد زیر توجه کنید :

private string firstName;
private string lastName;

public FullName
{
   get { return firstName + " " + lastName; }
}

همانطور که در مثال بالا مشاهده می‌کنید یک property فقط خواندنی تعریف کرده‌ایم که مقدار برگشتی آن ترکیبی از دو فیلد firstName و lastName است که به وسیله فاصله از هم جدا شده‌اند. سی شارپ همچنین یک راه حل کوتاه برای ایجاد property ارائه می‌دهد. در این روش می‌توانید یک property بدون فیلد ایجاد کنید. گاهی اوقات ممکن است که شما اصلاً نخواهید اعتبار سنجی انجام دهید. در این صورت بهتر است که آینده نگر باشید و باز هم به ازای هر فیلد موجود در کلاس یک خاصیت تعریف کنید. البته برای کاهش کدنویسی، می‌توانید از نوع خلاصه شده property ها یعنی property های خودکار استفاده کنید :

public int MyProperty { get; set; }

این ویژگی فراخوانی خودکار property نام دارد و در سی شارپ 3٫0 معرفی شده است. به این نکته توجه کنید که در این روش هیچ کدی برای بخش set و get نمی‌نویسیم. دستور بالا معادل تعریف یک فیلد از نوع int با سطح دسترسی private است و property مربوط در یک خط مختصر نوشته شده و اجرا می‌شود. کامپایلر کد بالا را به صورت خودکار به عنوان یک property شناسایی می‌کند و فیلد مربوط به آن در طول زمان اجرای برنامه ساخته می‌شود. توجه کنید وقتی یک property خودکار ایجاد می‌کنید باید هر دو بخش get و set آن را نیز تعریف کنید. همچنین نباید هیچ کدی در داخل این دو بخش بنویسید. بعدها اگر لازم بود که برای فیلدها اعتبارسنجی صورت بگیرد، می‌توانید بدون اینکه کدی که از کلاس استفاده می‌کند نیاز به تغییری داشته باشد، property های خودکار را به صورت property های معمولی ولی با اعتبارسنجی بنویسید.