תכנות מונחה עצמים (OOP) - מאפיינים (Properties)

סגור באמצעות טופס זה תוכלו לספר ולהמליץ לחבריכם..
שם השולח:
כתובת דוא"ל של השולח:
שם המקבל:
שלח לכתובת דוא"ל:
הוסף הערה:
#C מאפשרת להגדיר מתודות גישה מיוחדות הנקראות מאפיינים (Properties) באמצעות המאפיינים נוכל להעביר ערך לתכונות הפרטיות או לקבל מהן את ערכן.

 מאפיינים (Properties)
 
מאת: ארז קלר

הורדת דוגמאות קוד 

תכנון נכון של מחלקה מסתיר את התכונות (Data Members) מפני כל הגורמים החיצוניים למחלקה (Data Hiding), נקפיד שהתכונות יוגדרו פרטיות משום שאנו רוצים להגן על המידע

​אולם בלא מעט סיטואציות ניאלץ לשנות את ערכי התכונות: עיגול ישנה את גודלו או את מיקומו לאורך חייו, אדם מחליף כתובת, מכונית בזמן הנסיעה משנה תדיר את מהירותה ואת כיוון נסיעתה וכו', ערכי התכונות משתנים ללא הרף במהלך חיי האובייקט. 
מה קורה כאשר צריך לשנות באובייקט את אחד מפרטי המידע? 
במקרים אלו ורבים אחרים נזדקק למכניזם מסוים אשר יאפשר לשנות בזמן ריצה (Run time) את הערכים הקיימים ב- Data Members .
נרצה מכניזם שיאפשר לשנות את ערכי התכונות אולם מבלי לפגוע בעיקרון ההסתרה והפרטיות שלהן.

#C מאפשרת להגדיר מתודות גישה מיוחדות הנקראות מאפיינים (Properties) באמצעות המאפיינים נוכל להעביר ערך לתכונות הפרטיות או לקבל מהן את ערכן.

כפי שנראה בהמשך, המאפיינים מחולקים לשתי יחידות :
הראשונה נקראת Set ובאמצעותה ניתן לשנות את ערכי השדה (כתיבה לתכונה),
השנייה נקראת Get ובאמצעותה ניתן להחזיר את תוכן השדה (קריאה מהתכונה).
המאפיינים (Properties) מאפשרים לבצע את פעולות ה- Get וה- Set בצורה קריאה, ברורה, נוחה יותר ואינטואיטיבית.
ניתן להשתמש הן ב- Set מכונה  (Set Accessor) והן ב- Get המכונה (Get Accessor) עבור כל אחת מתכונות המחלקה או רק באחד מהם על פי הצורך.
מאפיין שמגדיר רק בלוק Get מכונה Read Only property,
מאפיין שמגדיר רק בלוק Set מכונה Write only property.
לא חובה להגדיר property עבור כל תכונות המחלקה.
בשפת #C ניתן להגדיר גם מאפיינים סטאטיים (Static Properties) עבור תכונות סטאטיות.

תחביר:
public Type PropertyName
{
     get
     {
          return var;
     }
     set
     {
          var= value;
     }
}
 

value הנו משתנה אוטומטי שמוגדר בתוך set  ונועד להשמת המידע החדש בשדה הרלוונטי.
Value  הנה מילה שמורה והטיפוס ממנו נוצר משתנה אוטומטי זה מוגדר ב – Prototype של ה – Property .


דוגמת קוד (PropertiesSample01):
 1  : class Point 
 2  : { 
 3  :     private int x; 
 4  :     private int y; 
 5  :     public Point() 
 6  :     { 
 7  :     } 
 8  :     public Point(int x, int y) 
 9  :     { 
 10 :         this.X = x; 
 11 :         this.Y = y; 
 12 :     } 
 13 :     public void Print() 
 14 :     { 
 15 :         Console.WriteLine("x = {0}, y = {1}", x, y); 
 16 :     } 
 17 :     public int X 
 18 :     { 
 19 :         get 
 20 :         { 
 21 :             return x; 
 22 :         } 
 23 :         set 
 24 :         { 
 25 :             if (value  >= 0 && value < = 1200) 
 26 :                 x = value; 
 27 :         } 
 28 :     } 
 29 :     public int Y 
 30 :     { 
 31 :         get 
 32 :         { 
 33 :             return y; 
 34 :         } 
 35 :         set 
 36 :         { 
 37 :             if (value  >= 0 && value < = 600) 
 38 :                 y = value; 
 39 :         } 
 40 :     } 
 41 : } 

בשורות 3 ו-4 נגדיר את התכונות ונקפיד שהם ישארו פרטיות (תמיד).
שורה 17 מוגדר המאפיין X, בעבור התכונה x, המאפיין מאפשר הן לשנות את ערכו של השדה x  באמצעות המקטע set (בשורה 23), והן להחזיר את ערכו של התכונה x באמצעות המקטע get (שורה 19).
שימו לב, שבתוך המקטע של set נקפיד לבדוק את נכונות/חוקיות המידע לפני שנבצע השמה.
בשורה 29 מוגדר מאפיין עבור התכונה y.

 1  : class Program 
 2  : { 
 3  :     static void Main(string[] args) 
 4  :     { 
 5  :         Point p1 = new Point(11, 22); 
 6  :         Point p2 = new Point(33, 44); 
 7  :         if (p1.X == p2.X && p1.Y == p2.Y) 
 8  :         { 
 9  :             Console.WriteLine("P1 and P2 are equals"); 
 10 :         } 
 11 :         p1.X = 110; 
 12 :         p1.Y = 220; 
 13 :         p2.X = 330; 
 14 :         p2.Y = 440; 
 15 :         p1.Print(); 
 16 :         p2.Print(); 
 17 :     } 
 18 : } 

השימוש ב- Properties, מספק תחושה נוחה  של גישה ישירה לתכונות המחלקה (Data Members), אולם זו תחושה מוטעית, הם אינם תכונות, אין להם כתובות זיכרון לאחסון המידע. 
השימוש בהם משפר את קריאות הקוד .

בשורה 7 מופעלים מקטעי ה-get של המאפיינים X ו- Y .
בשורות 11 עד 14 מופעלים מקטעי ה- set של המאפיינים X ו-Y.


פלט:
פלט תוכנית הדוגמה PropertiesSample01

המאפיינים הינם למעשה מתודות, הקומפיילר ממיר את המאפיין לשתי מתודות (אחת בשביל ה- set והשנייה בשביל ה- get).
ניתן לראות זאת באמצעות תוכנית השירות CodeReflect אשר חושפת ומציגה את הקוד בשפת MSIL:
כל אחד ממקטעי הקוד (get ו-set) של המאפיינים מתורגם למתודה על ידי הקומפיילר.
ניתן לראות את המאפיינים כתוסף "קוסמטי" של השפה, אולם, אין לזלזל ב"קוסמטיקה" שהשפה מציעה למפתח, חשיבותם היא לא בשיפור ביצועים, עדיין מדובר בהפעלת מתודות על כל המשתמע מכך, אלא בשיפור קריאות הקוד שמקל על התחזוקה, פחות שגיאות לוגיות, חיסכון בזמן ותחושה נעימה יותר של עבודה עם מחלקות קיימות.


מעבר לשימוש הסטנדרטי במאפיינים, קיימים עוד מספר מקרים בהם הנחיצות בהגדרת מאפיינים מאוד בולטת, לדוגמה:
סינון מידע - לעיתים ניעזר במאפיינים לצורך סינון המידע הזורם אל מחוץ לגבולות המחלקה מתוך בלוק get , ונבדוק את המידע המגיע לבלוק set לפני ביצוע ההשמה בפועל. לדוגמה: במידה ומחלקת לקוח מכילה שדה המגדיר מספר כרטיס אשראי, לא נרצה, מטעמי אבטחת המידע לחשוף אותו, ולכן מקובל לחשוף רק את ארבעת השדות האחרונות של כרטיס האשראי. 
שדות חישוביים - לעיתים נגדיר מאפיינים במקום להגדיר שדה חישובי, מאפיין המשמש כתחליף לשדה חישובי יכיל get בלבד.שדה חישובי הנו שדה שניתן להגיע לערך אותו הוא מכיל מתוך חישוב מתמטי המתבצע על שדות אחרים, לדוגמה: נגדיר שדה הנקרא תאריך לידה, אולם, לא נגדיר שדה הנקרא גיל משום שהוא פועל יוצא של חישוב המתבצע על השדה תאריך לידה.
הגדרת שדה חישובי כתכונה מהווה כר פורה ליצירת לשגיאות לוגיות במערכת. 
 
דוגמת קוד (PropertySample02):


 1  : class Customer 
 2  : { 
 3  :     private int id; 
 4  :     private string lastName; 
 5  :     private string firstName; 
 6  :     private DateTime birthdate; 
 7  :     private string creditCardNumber; 
 8  :     public Customer(int id) { this.id = id; } 
 9  :     public int ID 
 10 :     { 
 11 :         get { return id; } 
 12 :     } 
 13 :     public string LastName 
 14 :     { 
 15 :         get { return this.lastName; } 
 16 :         set { this.lastName = value; } 
 17 :     } 
 18 :     public string FirstName 
 19 :     { 
 20 :         get { return this.firstName; } 
 21 :         set { this.firstName = value; } 
 22 :     } 
 23 :     public DateTime Birthdate 
 24 :     { 
 25 :         get { return birthdate; } 
 26 :         set { birthdate = value; } 
 27 :     } 
 28 :     public string CreditCardNumber 
 29 :     { 
 30 :         get 
 31 :         { 
 32 :             string last_numbers = creditCardNumber.Substring(creditCardNumber.Length - 4); 
 33 :             return last_numbers; 
 34 :         } 
 35 :         set { creditCardNumber = value; } 
 36 :     } 
 37 :     public int Age 
 38 :     { 
 39 :         get 
 40 :         { 
 41 :             int year = DateTime.Now.Year; 
 42 :             return year - birthdate.Year; 
 43 :         } 
 44 :     } 
 45 :     public void Print() 
 46 :     { 
 47 :         Console.WriteLine("ID:{0},Last Name:{1},First Name:{2}", id, lastName, firstName); 
 48 :         Console.WriteLine("Birthdate:{0}", birthdate.ToShortDateString()); 
 49 :         Console.WriteLine("Credit Card : XXXX-{0}", CreditCardNumber); 
 50 :     } 
 51 : } 
בשורה 28 מוגדר המאפיין CreditCardNumber, מקטע ה- Get שלה אינו מחזיר את ערכו של השדה creditCardNumber אלא רק את ארבעת התוים האחרונים כפי שאנו מכירים מחשבוניות. 
בשורה 37 מוגדר המאפיין Age, בניגוד למה שראינו עד כה, המאפיין Age לא קשור ישירות לאף תכונה, הוא למעשה מעין שדה חישובי, ניתן להגיע לערכו על ידי חישוב שמתבצע על תוכנם של תכונות אחרות.



Main:
 1  : class Program 
 2  : { 
 3  :     static void Main(string[] args) 
 4  :     { 
 5  :         Customer customer = new Customer(123456); 
 6  :         customer.LastName = "Israeli"; 
 7  :         customer.FirstName = "Moshe"; 
 8  :         customer.CreditCardNumber = "1234-5678-2345-6789"; 
 9  :         customer.Birthdate = new DateTime(1981, 3, 23); 
 10 :         customer.Print(); 
 11 :     } 
 12 : } 

פלט:
פלט דוגמת הקוד PropertiesSample02


 



כל הזכויות שמורות למחבר ©