בית פורומים למתכנתים שבינינו

מדריך C# - שני

שלום אורח. באפשרותך להתחבר או להירשם
הצג 15 הודעות בעמוד הוסף לדף האישי  דווח למנהל שלח לחבר
נשלח ב-27/7/2011 10:33 לינק ישיר 
מדריך C# - שני

נלקח מהאתר: www.webmaster.org.il

מדריך C# - תכנות מובנה עצמים - מבוא

מאמר זה פותח סדרה של מאמרים בנושא תכנות מונחה עצמים (Object Oriented Programming). בניגוד לשאר המאמרים בסדרה זו, מאמר זה הינו תאורטי ואינו משלב קוד טכני ומטרתו היא להציג את הקונספט של תכנות מונחה עצמים.

שיטת Object Oriented – כל דבר הוא אובייקט

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

נסביר: בבואנו להסתכל על העולם – אנו באופן טבעי ואוטומטי מסווגים את כל העצמים על פי קטגוריות שונות. לדוגמא, העולם הפיזי מחולק על ידנו לחי, צומח ודומם. קיימת גם חלוקה פנימית יותר ובה את החי אנו מחלקים ליונקים, עופות, דגים וכך הלאה לחלוקות פנימיות יותר. כל עצם או מושג המוכר לנו בעולם הזה משתייך למספר רב של קטגוריות בסדר היררכי כלשהו.

למה? 
התשובה לכך פשוטה, כך קל ונח יותר לשלוט בכמות האדירה של האינפורמציה בה אנו מוצפים וכך יעיל ונח יותר לסדר אינפורמציה זו במוחנו, בכדי שנוכל לשלפה ולזהותה בקלות בכל עת שנחפוץ.

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

תכונה נוספת המאפיינת את תהליך החשיבה של האדם הינה – שיוך של פעולה לעצם כלשהו. למשל, נשווה בין שתי הפעולות הבאות: הדלקת נר והדלקת המחשב – לכאורה, מבחינה לוגית משמעותן של שתי הפעולות זהה – שתיהן פעולות "הדלקה", אך הנגזרת של כל פעולה היא מימוש פיזי אחר – כלומר, סידרת הצעדים אשר נבצע שונה לחלוטין בין פעולה אחת לאחרת.

ומכאן כשנתבקש לבצע פעולת "הדלקה" אנו נדע איזו פעילות עלינו לבצע על פי שיוך של הפעולה לעצם אותו אנו מתבקשים להדליק. כלומר, קיים קשר חשוב בין הפעולה לבין העצם עליו או איתו היא מתבצעת.

שני התהליכים החשיבתיים שהגדרנו אצל בני האדם מיושמים בצורה דומה בגישה מונחית העצמים ע"י יצירת טיפוס חדש המכונה Class (מחלקה) המאפשר ריכוז של מאפיינים (משתנים) ומטודות (פונקציות) תחת הגדרה אחת.

לאחר הגדרת המחלקה ניתן לייצר מופעים שלה, כל מופע של מחלקה קרוי Object (אובייקט). כאשר המחלקה הינה תבנית ליצירת אובייקטים מסוגה, כלומר, המחלקה מהווה את ההגדרה של המשתנים והשיטות שיכילו אח"כ כל מופעיה (האובייקטים מסוגה).

 

אלמנטים עיקריים של שיטת Object Oriented

כפי שהוסבר, השיטה המכוונת עצמים תומכת ובנויה ממחלקות ואובייקטים, אך קיימים בה מרכיבים עיקריים ומהותיים נוספים בבניית מערכות והם מהווים את משולש תכונותיה העיקריות:

clip_image002




תוקן על ידי אנוני_מוס ב- 27/07/2011 10:35:01



תוקן על ידי אנוני_מוס ב- 27/07/2011 10:36:32



תוקן על ידי אנוני_מוס ב- 27/07/2011 10:53:04



תוקן על ידי אנוני_מוס ב- 27/07/2011 11:14:45




דווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:33 לינק ישיר 

כימוס – Encapsulation

כימוס, הנקרא לעיתים הסתרת מידע, מאפשר הסתרת כל המרכיבים הפנימיים של האובייקט ומאפשר גישה אליהם אך ורק דרך הפונקציות של האובייקט המהוות את הממשק. 
מכאן שאנו מגנים על מאפייני האובייקט באי מתן גישה ישירה אליהם, אלא אך ורק דרך פונקציות ממשק שהוגדרו לאותו אובייקט והן לא מאפשרות לבצע במאפייניו שינויים בלתי הולמים או חוקיים.

כימוס מספק את הגבול שבין מאפייניו הפנימיים של האובייקט לבין הממשק החיצוני שלו, בכך שהוא לא מאפשר שינוי באופן ישיר של משתני האובייקט, אלא רק דרך פונקציות הממשק שלו.

מושג נוסף אשר מרכיב את הכימוס הוא מושג ההפשטה (Abstraction):

הפשטה (Abstraction)

הפשטה המהווה חלק מהכימוס, מתייחסת לאופן שבו מיוצגת בעיה נתונה במרחב התוכנית. שפות התכנות לכשעצמן מספקות הפשטה. אנו התוכניתנים לא עוסקים בפנים של המחשב, כלומר, מרחב זיכרון, אוגרים ומחסניות של המעבד.

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

וכפי ששפות התכנות העיליות יוצרות הפשטה של ההתנהגות הפיזית של המחשב ומאפשרות להשתמש במרחבי הזיכרון שלו בצורה שקופה, מבלי צורך להעמיק בחומרה שמרכיבה אותו, כך אנו צריכים לתכנן את המחלקות שמשתתפות במערכת שלנו כדי שיאפשרו עבודה נוחה וברורה יותר ללקוחות המחלקות שלנו (=אותם תוכניתנים שייצרו אובייקטים מסוג המחלקות).

מכאן, שעל מנת לאפשר ללקוחות המחלקה שלנו להתמקד במטרה ולא לעסוק בפרטים הנוגעים לדרך הפעולה של המחלקה עצמה, עלינו לתכנן הפשטה של המחלקה בדרך הטובה ביותר. ממשק המחלקה הוא המימוש של ההפשטה!

נסביר את החשיבות בעזרת אנלוגיה למכשיר כספומט המוכר לנו מחיי היום יום. מכשיר זה אמור לקבל כקלט כרטיס וסיסמא תואמת ולאפשר ללקוח משיכת מזומנים או הצגת פרטי חשבון רלוונטיים. למכשיר הכספומט יש קבוצת פונקציות שעליו להציג בפני המשתמשים. פונקציות אלו מהוות את הממשק למשתמש. ממשק זה מתבטא בחריץ להעברת הכרטיס, חריץ להוצאת הכסף או פלט, צג, מקלדת וכו' – בעצם כל אחד מאלו מהווה חלק ממשק המכשיר.

מכשירי הכספומט לא השתנו הרבה מאז המצאתם, זאת משום שלמרות העובדה שהמערכת הפנימית שלהם השתנתה עם התפתחות הטכנולוגיה, הרי שהממשק הבסיסי שלהם לא היה צריך להשתנות בהרבה, ובכך איפשר למשתמשי הכספומט לעבוד באותה צורה לה הורגלו.

חלק אינטגרלי של תכנון ממשק מחלקה הוא הבנה עמוקה של מרחב הבעיה כולה. הבנה זו תעזור לנו ליצור ממשק המספק למשתמשים גישה למידע שהם זקוקים לו, אך מבודד אותם מהעבודה הפנימית של המחלקה. עלינו לתכנן מחלקה שתאפשר שינוי של מאפייני המחלקה בלי שתהיה לכך השפעה על קוד קיים.

בנוסף לכך, אל לנו להשתמש במונחים זרים ללקוחות המחלקה שלנו כי אם במונחים טבעיים אשר יהיו ברורים גם ללקוחות שהם לא מתמחים בנושא המנוהל ע"י המחלקה. כמו כן עלינו להגן על משתני המחלקה בצורה כזו שכל שינוי שלהם יהיה דרך פונקצית ממשק, בכדי שנהיה בטוחים כי ננקטו כל אמצעי הזהירות, דבר שלכאורה לא בהכרח יידעו לקוחות המחלקה לבצע, מטבע הדברים של חוסר בקיאות במחלקה שלנו.

לסיכום - עיצוב ההפשטה של מחלקות בדרך המועילה ביותר לתוכניתנים המשתמשים בהן הוא בעל חשיבות עליונה בעיצוב תוכנה הניתנת לשימוש חוזר (Reuse). ממשק יציב, סטטי ועקבי לאורך שינויי יישום עתידיים של המחלקה מחייב פחות שינויים עם הזמן.

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



תוקן על ידי אנוני_מוס ב- 27/07/2011 10:35:30




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:36 לינק ישיר 

הורשה - Inheritance

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

על ידי הורשה יכול המתכנת להגדיר סוג של קשר בין מחלקה אחת לאחרת, באמצעות ההורשה ניתן לגזור מחלקה קיימת ובכך ליצור מחלקה חדשה אשר מכילה את הקיימת, בהמשך ניתן לשנות את המחלקה החדשה בדרך הרצויה.

המחלקה אותה אנו יורשים נקראת מחלקת הבסיס (Base Class) והמחלקה החדשה שנוצרה ע"י מחלקת הבסיס נקראת מחלקה נגזרת (Derived Class). המחלקה הנגזרת יורשת את כל החברים (משתנים ופונקציות) של מחלקת הבסיס.

יכולת זו מאפשרת חיסכון בכתיבת קוד ושימוש בקוד קיים. נדגים - אנו מתכננים מערכת לניהול אוניברסיטה כלשהי ומכאן שחלק מן הישויות המעורבות הן: סטודנטים, מרצים ועובדי מנהלה. עבור כל אחת מישויות אלו נצטרך לשמור את התכונות הבאות: שם פרטי, שם משפחה, תעודת זהות, כתובת וכו'. בנוסף, לכל אחת מהישויות נצטרך לשמור פרטים נוספים שונים המייחדים אותה – לדוגמא: שנת לימוד לישות סטודנט, תעריף לשעה לישות מרצה ומשכורת בסיס לישות עובד מנהלה.

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

Without Inheritance

בתרשים זה מתוארות המחלקות ללא השימוש בהורשה, כאשר אנו רואים כאן את החזרה על התכונות המשותפות בכל מחלקה, כמו כן תהיה חזרה על פונקציות הממשק המנהלות תכונות אלו בכל מחלקה (לא מתואר בשרטוט).

פתרון אחר יעיל יותר, הן מבחינת חיסכון בקוד והן מבחינת ניהול עקבי שלו, הוא פיתרון בו נגדיר ישות אדם אשר תכיל את כל התכונות ופונקציות הממשק שמשותפות לכל הישויות, ובהמשך כל אחת משלושת הישויות תירש את מחלקת האדם (מחלקת הבסיס) ותוסיף לה את התכונות והפונקציות המייחדות אותה. 

with Inheritance
בתרשים זה מתוארות המחלקות עם שימוש בהורשה, כאשר אנו רואים שכל המידע המשותף מנוהל במחלקת בסיס אותה יורשות המחלקות הנגזרות. כל אחת מהמחלקות הנגזרות תוסיף על התכונות שהיא ירשה את התכונות המייחדות אותה ובנוסף תשנה ותוסיף את פונקציות הממשק הדרושות לה.

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

דבר נוסף חשוב מאד, אותו נבין רק בתום קריאת הפרק הבא והוא שההורשה היא זו שמאפשרת את ה- Polymorphism.



תוקן על ידי אנוני_מוס ב- 27/07/2011 10:34:56




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:36 לינק ישיר 

רב-צורתיות - Polymorphism

בתחילה נעזר בדוגמא על מנת להמחיש את מטרת עקרון הרב-צורתיות. 
נניח ואנו מתכננים מערכת לניהול מוסך. המוסך שלנו מטפל ברכבים מהסוגים הבאים: אוטובוסים, רכבים פרטיים ומשאיות. בהתאם לתכונה ההורשה שלמדנו קודם אנו מניחים כי נגדיר מחלקת בסיס Car שתכיל את כל הנתונים המשותפים וממנה יגזרו 3 המחלקות: Truck, Private ו- Bus, כאשר כל אחת מהמחלקות תוסיף ותעדכן את התכונות הייחודיות לה. 
מחלקת Garage (מוסך) תכיל מבנה נתונים שינהל את כל הרכבים שנמצאים ברגע נתון מסוים בטיפול במוסך, נניח ומבנה הנתונים שלנו יוחזק בתוך מערך .

מכאן, שהמערך שלנו צריך להחזיק ולנהל אובייקטים מסוגים שונים (Truck, Private או- Bus)!

נסביר: רב-צורתיות מספקת יתרון בכך שהיא מעניקה את היכולת לקבץ אובייקטים להם יש מחלקת בסיס משותפת ולהתייחס אליהם בצורה עקבית.

אם נתבונן בדוגמא, נשים לב כי המשותף ל- 3 המחלקות (Truck, Private ו- Bus) היא מחלקת הבסיס Car, מכאן שכל אחת ממחלקות אלו מכילה בתוכה את מאפייני מחלקת Car ועל כן, כל אחת ממחלקות אלו היא גם מחלקת Car.

עיקרון ה- Polymorphism מאפשר להתייחס לכל המחלקות שנגזרו ממחלקת בסיס מסוימת כמחלקות מאותו סוג, הלא הוא מחלקת הבסיס, ובהתאם לכך ליצור מבנה נתונים של אובייקטים מסוג מחלקת הבסיס אשר יכול להכיל את האובייקטים מהסוגים השונים – מחלקת הבסיס ונגזרותיה. יש לציין כי בסיוע של המרה (casting) מתאימה או מנגנונים מיוחדים נוכל להתייחס לאובייקט מסוים במבנה הנתונים בהתאם לסוגו האמיתי (המחלקה שהוא מייצג מופע שלה) ולא רק כאל אובייקט מסוג מחלקת הבסיס.

למשל, כאשר נתאר מחלקה של יונקים (מחלקת הבסיס) נשים לב בוודאי כי "אכילה" היא פעולה חיונית לקיומם. כלומר, כל סוגי היונקים (מחלקות נגזרות) חייבים לדעת לבצע את הפונקציה Eat, מאידך כל אחד מהיונקים מייצג מימוש שונה לפונקציה Eat – הרי כל אחד אוכל דברים שונים בדרכים שונות. נניח כי אנו מחזיקים רשימה מקושרת של יונקים מהסוגים השונים (אפשרי להחזיק מבנה נתונים של אובייקטים שונים אם להם מחלקת בסיס משותפת).

ריבוי צורות היא האפשרות לקחת עצם (אובייקט) מטיפוס יונק (אחד כלשהו מתוך הרשימה) ולהורות לו לאכול (Eat) כאשר אותו עצם יבצע את הפעולה המתאימה לו ביותר – כלומר, תופעל הפונקציה Eat שהוגדרה עבורו! זוהי טכניקת תכנות המבצעת את הפונקציה של המחלקה המתאימה.

מבנה השפה המאפשר את ריבוי הצורות הוא הכריכה הדינמית (Dynamic Binding), 
כאשר כריכה דינמית (או מאוחרת) היא האפשרות לקשור בין קריאה לפונקציה כלשהי לבין מימוש כלשהו של הפונקציה בזמן ריצה ולא בזמן ה- Link.

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

Polymorphism

בתרשים זה מתוארת היררכית המחלקות, כאשר בדוגמא זו התעלמנו ממאפייני כל מחלקה והמאפיינים המשותפים הנמצאים במחלקת הבסיס Mammal . ניתן לשים לב כי בכל אחת מהמחלקות מוגדרת פונקצית Eat בעלת מימוש שונה! (יש לציין כי במחלקת Mammal המימוש יכול להיות ריק או לא קיים כלל) .

נציג את הרשימה שהוזכרה בדוגמא לעיל, כאשר סוג טיפוס המצביעים מצביע ל- Mammal (כפי שהוסבר, כל המחלקות הנגזרות הן גם מסוג מחלקת הבסיס). כאשר, בזמן ריצת התוכנית כל אחד ממצביעים אלו הוקצה דינמית לטיפוס כלשהו מסוג המחלקות הנגזרות. ובזכות תכונת הכריכה המאוחרת (דינמית) המאפשרת את עיקרון ה- Polymorphism, כשנבצע קריאה לפונקציה Eat באמצעות צומת כלשהי ברשימה המקושרת – תופעל הפונקציהבהתאם לסוג ההצבעה (Cow/Person/Lion) ולא בהתאם לסוג המצביע (Mammal), משמע, מערכת זמן הריצה תבטיח קריאה לשיטה (פונקציה) מתאימה של האובייקט הנגזר.

יתרון חשוב נוסף של הרב צורתיות הוא שקוד ישן יכול להשתמש בקוד חדש. כלומר, גם אם נוסיף עכשיו סוג חדש של יונק שנגזר ממחלקת יונק, נוכל להכניס אותו לרשימה והקוד כולו ימשיך לפעול מבלי שהיה צורך לשנות אותו.

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





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:51 לינק ישיר 
מדריך C# - תכנות מובנה עצמים - יצירת מחלקה בסיסית

מחלקה היא תבנית סגורה המכילה משתנים ופונקציות, אשר ניתן ליצור ממנה הרבה מופעים (אובייקטים). מחלקה מאפשרת לממש את עקרון הכימוס (encapsulation) - היכולת להסתיר אלמנטים הקיימים בתוך המחלקה, כך שרק לחברי המחלקה תהיה גישה אליהם.

כדי להוסיף מחלקה חדשה לפרויקט יש ללחוץ על לחצן העכבר הימני על הפרויקט (בחלון ה- Solution Explore) ולבחור Add –> Class. בחלון שיפתח יש לתת שם למחלקה. פעולה זו יוצרת מחלקה חדשה בקובץ חדש בפרויקט. ב C# ניתן גם לכתוב מספר מחלקות בקובץ אחד, אך לצורך התמצאות קלה בפרויקט זה לא מומלץ.





 נהוג להתחיל שמות של מחלקות ושמות של פונקציות באות גדולה באנגלית (למשל, Person).

 נהוג להתחיל שמות של משתנים באות קטנה באנגלית (למשל, firstName).

כמו שניתן לראות מבנה המחלקה הוא:

  class Person
  {
    //כל מה שנכתוב כאן שייך למחלקה
  }

יצירת מחלקה עם משתנים:

  class Person
  {
    public string firstName;
    public string lastName;
    public int id;
  }
 הערה: בד"כ משתנים יוגדרו כ- private וחשיפתם מחוץ למחלקה תתבצע באמצעות properties מתאימים. נראה זאת בהמשך.

יצירת אובייקט מהמחלקה:

Person p = new Person(); 

שימוש במשתנים להשמת ערכים:

p.FirstName = "Lior";
p.LastName = "Zamir";
p.Id = 1234578889;

שימוש במשתנים לשליפת ערכים:

Console.WriteLine("Name: {0} {1}", p.FirstName, p.LastName);

כתיבת פונקציות בתוך מחלקה:

class Person
{
  public string firstName;
  public string lastName;
  public int id;

  public void Print()
  {
    Console.WriteLine("Full Name: {0} {1}", firstName, lastName);
  }
}

הפעלת הפונקציה מהאובייקט:

p.Print();




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:51 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: שימוש ב- Properties

Property הינו מנגנון המאפשר לממש את עקרון הכימוס (encapsulation). הרעיון הוא להסתיר את המשתנים ולחשוף אותם דרך מנגנון סינון ובקרה לצורכי בדיקת תקינות המידע. בכדי להסתיר את המשתנה יש להגדיר אותו כ- private ואת ה- Property שמאפשר את הגישה למשתנה יש להגדיר כ- public. ניתן לגשת למשתנה private אך ורק מתוך המחלקה.

  נהוג ששם המשתנה יתחיל באות קטנה ושם ה- property יהיה אותו השם, אך יתחיל באות גדולה.


  class Person
  {
    //Data members:

    private string firstName;
    private string lastName;
    private int id;

    //Properties:

    public string FirstName
    {
      get { return firstName; }
      set { firstName = value; }
    }

    public string LastName
    {
      get { return lastName; }
      set { lastName = value; }
    }

    public int Id
    {
      get { return id; }
      set { id = value; }
    }

    //Methods:

    public void Print()
    {
      Console.WriteLine("Name: {0} {1}", firstName, lastName);
    }
  }

שימו לב שאין צורך לשנות את הפונקציה Print כי היא נמצאת בתוך המחלקה ולכן יכולה לגשת למשתני ה – private. לעומת זאת הקוד שעובד עם האובייקט חייב להשתנות כי אין לו גישה למשתנים:

      Person p = new Person();

      p.FirstName = "Lior";
      p.LastName = "Zamir";
      p.Id = 123456789;

      p.Print();

כאשר נכניס ערך לתוך ה- property יופעל החלק של ה - set:

      p.FirstName = "Lior";

וכאשר נקבל את הערך מה- property יופעל החלק של ה - get:

      string name = p.FirstName;

כעת ניתן לבצע סינון מידע כדי שלא יכנס מידע שגוי לאובייקט, לדוגמא נניח ונרצה לאכוף על הערך של id את החובה שלא יכיל יותר מ- 9 ספרות, נעשה זאת כך: 

    public int Id
    {
      get { return id; }
      set
      {
        if (value > 999999999)
          return;

        id = value;
      }
    }

הערה:

בשלב זה יש לבצע את הבדיקות ב- property ללא שימוש במנגנון Exception Handling, שילמד בהמשך.

ניתן ליצור גם Property שיכיל רק אחד מהחלקים get, set אם נרצה שלא לאפשר השמה או קבלה של הערך.

לדוגמא readonly property, אשר מאפשר רק לקבל את הערך, אך לא מאפשר לבצע השמה:

    public int Id
    {
      get { return id; }
    }

בדוגמא זו אם ננסה לבצע את הפעולה הבאה, נקבל שגיאת קומפילציה:

p.Id = 123456789;




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:53 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: שימוש ב- Constructors

בנאי (Constructor. נקרא גם בקיצור c'tor) הוא למעשה פונקציה לבנייה התחלתית של האובייקט. הוא מופעלאוטומטית בעת יצירת מופע מהמחלקה (אובייקט) באמצעות הפקודה new:

    Person p = new Person();

המטרה של בנאי היא לבצע פעולות בזמן יצירת האובייקט, כגון: אתחול משתנים.

מספר דברים שצריך לדעת לגבי בנאי:

  • שם הבנאי יהיה זהה לשם המחלקה המכילה אותו (case sensitive).
  • בנאי לא מחזיר ערך (גם לא void)
  • בנאי יכול לקבל פרמטרים
  • ניתן להעמיס (overload) בנאי (כך שלכל מחלקה יכולים להיות מספר בנאים. לכל הבנאים יהיה שם זהה, ההבדל יהיה במספר הפרמטרים שהבנאי מקבל ובסוגם).
  • ניתן לקרוא מבנאי אחד לאחר
  • בקריאה מבנאי אחד לאחר חובה לבצע זאת בשורה הראשונה של הבנאי
  • אם לא נכתוב אף בנאי במחלקה יהיה בנאי default (ברירת מחדל) שלא מקבל פרמטרים ולא מבצע שום פעולה.
  • מספיק שנכתוב בנאי אחד במחלקה (לא משנה עם איזה פרמטרים) אנו מוותרים על הבנאי ברירת מחדל.

דוגמא ליצירת בנאי שמקבל פרמטרים:

    public Person(string firstName, string lastName)
    {
      FirstName = firstName;
      LastName = lastName;
    }

שימו לב שההתייחסות היא למשתנה firstName של הפונקציה (המוגדר בכותרת הפונקציה) ולא של המחלקה. אם נרצה לגשת למשתנה המחלקה ניתן לכתוב: this.firstName

הפעלת הבנאי שמקבל פרמטרים:

      Person p = new Person("Lior", "Zamir");

יצירת בנאי המקבל פרמטרים ומעביר את חלקם לבנאי הקודם (שימו לב לשימוש במילת המפתח this):

   public Person(string firstName, string lastName, int id)
       : this(firstName, lastName)
   {
     Id = id;
   }

במקרה זה, הבנאי מקבל שלושה פרמטרים, מהם הוא שולח שניים לטיפול בבנאי הראשון, כך שנותר לו לטפל רק בפרמטר השלישי (id).




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:55 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: אלמנטים סטטיים

משתנה סטטי

כאשר נגדיר משתנה בתוך מחלקה, הוא למעשה נוצר מספר פעמים, פעם אחת בכל אובייקט שניצור. לעיתים נרצה להגדיר משתנים אשר מוגדרים פעם אחת לכל האובייקטים מסוג מסוים. לדוגמא: משתנה שיכיל את מספר האובייקטים שנוצרו מסוג (מחלקה) מסוים. משתנה כזה נקרא משתנה סטטי (static).

  • משתנה סטטי הוא משתנה שיש לו רק מופע אחד בלבד והוא משותף לכל המחלקה (בניגוד למשתנה רגיל ה'משוכפל' עבור כל אובייקט מהמחלקה).
  • משתנה סטטי נמצא רק פעם אחת בזיכרון, לא משנה כמה מופעי אובייקטים יש למחלקה.
  • כדי להגדיר משתנה כסטטי יש להוסיף את המילה השמורה static בעת ההצהרה עליו.

להלן דוגמא להגדרת משתנה static אשר יכיל את מספר האובייקטים שנוצרו מהמחלקה Grade, כך שבכל פעם שניצור אובייקט (בתוך ה- c'tor) המשתנה יקודם ב- 1 (לצורך הפשטות מופיע רק הקוד הרלוונטי):


  class Grade
  {
    public static int counter;

    public Grade()
    {
      //...
      counter++;
    }
  }

בכדי לגשת מהתוכנית לערך של המשתנה הסטטי, יש לפנות אליו דרך שם המחלקה ולא דרך שם האובייקט כמו שפונים למשתנה רגיל. שימו לב, שאין צורך כלל ליצור אובייקט כדי לגשת למשתנה סטטי:

Console.WriteLine(Grade.counter);

מאפיין סטטי

באותה שיטה ניתן להגדיר כל אלמנט שלמדנו עליו בנושא מחלקות כסטטי:  Property, Method, C'tor.

כדי להגדיר מאפיין (Property) כסטטי יש להוסיף את המילה השמורה static בכותרת המאפיין. 

להלן דוגמא להגדרת static property . המאפיין הסטטי חושף את המשתנה הסטטי:

    private static int counter;

    public static int Counter
    {
      get { return counter; }
    }

גם כאן הגישה היא באותה שיטה (דרך שם המחלקה):

    Console.WriteLine(Grade.Counter);

פונקציה סטטית

פונקציה סטטית היא פונקציה שמופעלת על המחלקה כולה ולא על מופע בודד (אובייקט) של המחלקה.

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

דוגמא לפונקציה סטטית המחשבת ממוצע (ולשם כך משתמשת במשתנים סטטיים):

  class Grade
  {
    //Static Data members:
    static int counter;
    static int sum;

    //Static Method:
    public static double Average()
    {
      return (double)sum / counter;
    }
  }

קריאה לפונקציה זו סטטית תתבצע כך:

Console.WriteLine(Grade.Average());


מחלקה סטטית

ניתן להגדיר גם static class, המשמעות היא:

  • כל מה שנמצא במחלקה חייב להיות static (משתנים, פונקציות, מאפיינים...)
  • לא ניתן ליצור מופעים (אובייקט) מהמחלקה
  • לא ניתן לרשת מהמחלקה

משתמשים במחלקה סטטית בעיקר ליצירת מחלקות עזר שיכילו פונקציות שירות. דוגמא למחלקות כאלו מתוך ה- NET Framework. :

  • Math – פונקציות וקבועים מתמטיים
  • Console – לעבודה עם מסך ה- Console



דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 10:57 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: קבועים ( Constants ו- Readonly)


constants (קבועים) הם תאים בזיכרון אשר מכילים ערך קבוע שאינו ניתן לשינוי (בניגוד למשתנים שמטבעם כן יכולים להשתנות). קבועים ב – NET. למעשה מוגדרים אוטומטית כ- static (אין צורך לציין זאת במפורש), הדבר אומר שהגישה אליהם היא דרך שם המחלקה ולא דרך אובייקט.

הגדרת const:

  class MyConst
  {
    public const double PI = 3.14159;
  }

גישה ל - const:

   Console.WriteLine(MyConst.PI);

Readonly

readonly (קריאה-בלבד) הינו משתנה קבוע, אשר בניגוד ל- const אינו מוגדר כ- static אלא מוגדר עבור כל אובייקט. זאת אומרת שהערך שלו יכול להיות שונה בכל מופע של האובייקט, אך מרגע שנוצר האובייקט לא ניתן לשנות אותו. ניתן להכניס לתוכו ערך בשורת ההצהרה עליו (כלומר אתחול כמו const) ו / או בבנאי (c'tor) בלבד. לאחר האתחול ובניית המופע של המחלקה לא ניתן עוד לשנות את ערכו של משתנה זה.

להלן דוגמא להגדרת readonly והשמת ערך בבנאי.

  שימו לב שהפונקציה ()ChangeValue תגרור שגיאת קומפילציה כי ניתן לבצע השמה רק באתחול או בבנאי:

class TestReadonly
  {
    //read-only data-member:
    private readonly int val;

    //read-only Property:
    public int Val
    {
      get { return val; }
    }

    //Ctor:
    public TestReadonly(int v)
    {
      val = v;
    }

    //Method:
    public void ChangeValue()
    {
      val = 10;  // !שגיאת קומפילציה
    }
  } 


 שימו לב שבד"כ למשתנה read-only יהיה property שהוא read-only (כלומר, ללא חלק של set), מיכוון שלא ניתן לשנות את ערך המשתנה לאחר שהוא כבר נוצר ואותחל.

דוגמא לגישה למשתנה readonly - הגישה היא דרך שם האובייקט (בניגוד ל - const). ניתן לשים לב שבכל אובייקט יש ערך אחר למשתנה:

      TestReadonly t1 = new TestReadonly(10);
      TestReadonly t2 = new TestReadonly(12);

      Console.WriteLine(t1.Val);
      Console.WriteLine(t2.Val);



תוקן על ידי אנוני_מוס ב- 27/07/2011 10:59:43




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:01 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: הורשה - Inheritance

הורשה הינה אחד מהעקרונות החשובים ביותר בתכנות מונחה עצמים (OOP). הורשה מאפשרת לממש את עקרון ה- reuse – שימוש חוזר בקוד ללא צורך לכתוב אותו מספר פעמים.

מימוש הורשה

בכדי לממש הורשה ב C# יש לכתוב במחלקה היורשת את מחלקת הבסיס שלה בצורה הבאה:

  class Base
  {

  }

  class Derived : Base
  {

  }

דגשים חשובים לגבי הורשה:

  • כל מה שיש במחלקה עובר בהורשה כולל: משתנים, properties, פונקציות וכו'. למעט בנאים (c'tors).
  • אלמנטים שמוגדרים כ- private עוברים בהורשה אך לא ניתן לגשת אליהם מהמחלקה היורשת.
  • השימוש במחלקה היורשת הוא כשימוש במחלקה רגילה.
  • במחלקה היורשת חובה להפעיל בנאי כלשהו ממחלקת הבסיס.
  • הבנאי של מחלקת הבסיס יופעל קודם לבנאי של המחלקה היורשת.
  • אם לא נציין במפורש איזה בנאי נרצה להפעיל יופעל אוטומטית הבנאי שאינו מקבל פרמטרים.

הפעלת בנאי ממחלקת הבסיס מתבצעת באמצעות המילה השמורה base ובאותה שיטה בה השתמשנו להפעלת בנאי אחר מאותה מחלקה (באמצעות this):

  class Base
  {
    public Base(int x)
    {
      //...
    }
  }

  class Derived : Base
  {
    public Derived(int x, int y)
      : base(x)
    {
      //...
    }
  }

דריסת פונקציות

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

לדוגמא:

  class Base
  {
    public void Print()
    {
      //...
    }
  }

  class Derived : Base
  {

    public new void Print()
    {
      //...
      base.Print();
      //...
    }
  }

דוגמא מלאה להורשה:

להלן דוגמא מלאה להורשה הכוללת מחלקת בסיס בשם Person ושתי מחלקות יורשות בשם Employee,Student.

דוגמא זו כוללת את כל הנושאים במאמר זה כולל הפעלת בנאי ודריסת פונקציות וכן דוגמא לשימוש בכל המחלקות.

תרשים המחלקות:

תרשים המחלקות - הורשה


  class Person
 
{
    //Fields:
    string firstName;
    string lastName;

    //Properties:

    public string FirstName
    {
      get { return firstName; }
      set { firstName = value; }
    }

    public string LastName
    {
      get { return lastName; }
      set { lastName = value; }
    }

    //C'tors:

    public Person()
      : this("", "")
    {
    }

    public Person(string firstName, string lastName)
    {
      FirstName = firstName;
      LastName = lastName;
    }

    //Methods:
    public string Print()
    {
      return string.Format("\nName: {0} {1}",
                           FirstName, LastName);
    }
  }
  class Employee : Person
  {
    //Fields:
    private double salary;

    //Properties:
    public double Salary
    {
      get { return salary; }
      set
      {
        if (value < 0)
          throw new Exception("Illegal salary");

        salary = value;
      }
    }

    //C'tors:
    public Employee()
    {
      Salary = 0;
    }

    public Employee(string fName, string lName, double salary)
      : base(fName, lName)
    {
      Salary = salary;
    }

    //Methods:
    public new string Print()
    {
      return base.Print()
             + string.Format("\nSalary: {0}", Salary);
    }
  }

  class Student : Person
  {
    //Fields:
    private int[] grades;

    //Properties:
    public int[] Grades
    {
      get { return grades; }
      set { grades = value; }
    }

    //C'tors:
    public Student()
    {
      Grades = new int[5];
    }

    public Student(string fName, string lName, int[] grades)
      : base(fName, lName)
    {
      Grades = grades;
    }

    //Methods:
    public new string Print()
    {
      string str = base.Print() + "\nGrades:\t";

      foreach (int g in Grades)
        str += g + "\t";

      return str;
    }
  } 

  class Program
  {
    static void Main(string[] args)
    {
      Employee e1 = new Employee();
      Employee e2 = new Employee("Lior", "Zamir", 25000);

      Console.WriteLine("Employees:");
      Console.WriteLine(e1.Print());
      Console.WriteLine(e2.Print());

      int[] grades = new int[] { 90, 100, 85 };

      Student s1 = new Student();
      Student s2 = new Student("Avi", "Levi", grades);

      Console.WriteLine("\n--------------\n\nStudents:");
      Console.WriteLine(s1.Print());
      Console.WriteLine(s2.Print());

    }
  }

פלט התוכנית:




תוקן על ידי אנוני_מוס ב- 27/07/2011 11:01:15




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:03 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: רב צורתיות - Polymorphism

פולימורפיזם (רב-צורתיות) הינה היכולת לתת מימוש שונה לאותה הפונקציה במחלקה היורשת כך שבזמן ריצה תופעל הפונקציה שמתאימה לטיפוס האובייקט. גם בנושא זה דנו במאמר המבוא ל- OOP ובמאמר זה נלמד איך לממש זאת. פולימורפיזם מתבסס על הורשה ואין פולימורפיזם ללא הורשה.

מימוש Polymorphism

בכדי לממש פולימורפיזם יש תחילה להגדיר את הפונקציה במחלקת הבסיס כוירטואלית (virtual) ובמחלקה היורשת להגדיר לדרוס אותה באמצעות override. לאחר מכן יש ליצור ייחוס ממחלקת הבסיס שיצביע על אובייקט מסוג המחלקה היורשת ולהפעיל את הפונקציה.

שימו לב שלא ניתן לבצע override אם הפונקציה במחלקת הבסיס לא הוגדרה ב- virtual ובדריסה רגילה, כפי שלמדנו בפרק ההורשה (באמצעות new) לא תופעל הפונקציה המתאימה לאובייקט.

בנוסף חשוב לדעת שפונקציה שביצענו ל- override היא גם וירטואלית לדור הבא שירש אותה.


בדוגמא הבאה תופעל הפונקציה Print השייכת למחלקה היורשת:


  class Program
  {
    static void Main(string[] args)
    {
      Base b = new Derived();
      Console.WriteLine(b.Print());
    }
  }

  class Base
  {
    public int X { get; set; }

    public virtual string Print()
    {
      return "Base print";
    }
  }

  class Derived : Base
  {
    public int Y { get; set; }

    public override string Print()
    {
      return "Derived print";
    }
  }

גישה לאובייקט

כאשר הייחוס הוא מסוג הבסיס והאובייקט הוא מסוג יורש לא ניתן לגשת לאלמנטים של היורש, אם ננסה לעשות זאת תתקבל שגיאת קומפילציה. אם בכל זאת נרצה לעשות נצטרך להצביע על האובייקט עם ייחוס המתאים לאובייקט (מסוג היורש).

לדוגמא אם ננסה לבצע זאת נקבל שגיאת קומפילציה:

      b.Y = 10;

נצטרך לבצע זאת כך:

      Derived d = (Derived)b;
      d.Y = 10;

או בקיצור:

      ((Derived)b).Y = 10;

שימושים של Polymorphism:

  • יצירת מערך הטרוגני – ניתן ליצור מערך מסוג הבסיס כך שכל איבר במערך יהיה אובייקט מסוג יורש וכאשר נרוץ על המערך ונפעיל את הפונקציה, תופעל הפונקציה המתאימה לאובייקט. היתרון הוא שלא נצטרך לבדוק עבור כל איבר מה הטיפוס שלו.
  • קבלה לפונקציה – ניתן לבנות פונקציה המקבלת משתנה מסוג בסיס ולשלוח אליה בכל פעם אובייקט מסוג אחר היורש את הבסיס.

דוגמא מלאה ל- Polymorphism:

להלן דוגמא מלאה הכוללת מימוש של פולימורפיזם בהורשה עם מספר דורות ושימושים של פולימורפיזם.

תרשים המחלקות:

פולימורפיזם - תרשים מחלקות

  class Person
  {
    //Fields:
    string firstName;
    string lastName;

    //Properties:

    public string FirstName
    {
      get { return firstName; }
      set { firstName = value; }
    }

    public string LastName
    {
      get { return lastName; }
      set { lastName = value; }
    }

    //C'tors:

    public Person()
      : this("", "")
    {
    }

    public Person(string firstName, string lastName)
    {
      FirstName = firstName;
      LastName = lastName;
    }

    //Methods:
    public virtual string Print()
    {
      return string.Format("Name: {0} {1}",
                           FirstName, LastName);
    }
  }

 class Student : Person
  {
    //Fields:
    private int[] grades;

    //Properties:
    public int[] Grades
    {
      get { return grades; }
      set { grades = value; }
    }

    //C'tors:
    public Student()
    {
      Grades = new int[5];
    }

    public Student(string fName, string lName, int[] grades)
      : base(fName, lName)
    {
      Grades = grades;
    }

    //Methods:
    public override string Print()
    {
      string str = base.Print() + "\nGrades:\t";

      foreach (int g in Grades)
        str += g + ";";

      return str;
    }
  }

class Employee : Person
  {
    //Fields:
    private double salary;

    //Properties:
    public double Salary
    {
      get { return salary; }
      set
      {
        if (value < 0)
          throw new Exception("Illegal salary");

        salary = value;
      }
    }

    //C'tors:
    public Employee()
    {
      Salary = 0;
    }

    public Employee(string fName, string lName, double salary)
      : base(fName, lName)
    {
      Salary = salary;
    }

    //Methods:
    public override string Print()
    {
      return base.Print() +
             string.Format("\nSalary: {0}", Salary);
    }
  }

 class Manager : Employee
  {
    //Fields:
    private double bonus;

    //Properties:
    public double Bonus
    {
      get { return bonus; }
      set
      {
        if (value < 0)
          throw new Exception("Illegal bonus");

        bonus = value;
      }
    }

    //C'tors:
    public Manager()
    {
      Bonus = 0;
    }
    public Manager(string fName, string lName,
                   double salary, double bonus)
      : base(fName, lName, salary)
    {
      Bonus = bonus;
    }

    //Methods:
    public override string Print()
    {
      return base.Print() +
             string.Format("\tBonus: {0}", Bonus);
    }
  }

 class Program
  {
    static void Main(string[] args)
    {
      Person[] pArr = new Person[5];
      pArr[0] = new Person("A","AA");
      pArr[1] = new Employee("B", "BB",10000);
      pArr[2] = new Student("C", "CC",new int[]{80,100,90});
      pArr[3] = new Person("D", "DD");
      pArr[4] = new Manager("E", "EE",20000,5000);

      foreach (Person p in pArr)
      {
        Console.WriteLine("\n"+p.GetType().Name);
        Console.WriteLine(p.Print());
      }

      Person p1 = new Person("Avi", "Levi");
      Manager m1 = new Manager("Lior", "Zamir", 20000,5000);

      Console.WriteLine("\n---------\nPrinting Person:");
      PrintPerson(p1);

      Console.WriteLine("\nPrinting Manager:");
      PrintPerson(m1);
    }

    static void PrintPerson(Person p)
    {
      Console.WriteLine(p.Print());
    }
  }

פלט התוכנית:

פולימורפיזם - פלט





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:03 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: אוספים מותאמים וסדרנים (Indexers)

לעיתים נרצה לבנות Collection (אוסף) משלנו אשר יכיל בתוכו אובייקטים מסוג שאנו נבנה ויספק לנו את הפונקציונאליות הקשורה לאובייקטים שלנו. ישנם הרבה דרכים ליצור Custom collection, החלק מלבנות מאפס ועד לרשת מ- Collection קיים, לכל שיטה היתרונות והחסרונות שלה. לצורך ההבנה של הדברים בצורה מיטבית נתמקד בשיטה של לעטוף Collection קיים.

הרעיון הוא לבנות מחלקה אשר תחזיק בתוכה משתנה מסוג ArrayList ותחשוף החוצה את הפונקציונאליות הנדרשת.

מכיוון שאנו בונים מחלקה ישנה בעיה בגישה אליה באמצעות סוגריים מרובעים [ ] מכיוון שניתן לגשת רק למערכים באמצעות סוגריים מרובעים. לכן במחלקת ה- collection שלנו נבנה property מיוחד שנקרא indexer (סדרן) המאפשר גישה באמצעות [ ].

להלן דוגמא מלאה ל- Custom Collection עבור המחלקות שבנינו בפרק הפולימורפיזם, כולל מימוש indexer ושימוש ב- Collection:


using System.Collections;

namespace CustomCollections
{
  class PersonCollection : IEnumerable
  {
    //Fields:
    private ArrayList pArr;

    //C'tors:
    public PersonCollection()
    {
      pArr = new ArrayList();
    }

    //Properties:
    public int Count
    {
      get
      {
        return pArr.Count;
      }
    }

    //Indexers:

    public Person this[int idx]
    {
      get
      {
        return (Person)pArr[idx];
      }
      set
      {
        pArr[idx] = value;
      }
    }

    public Person this[string fName]
    {
      get
      {
        for (int i = 0; i < Count; i++)
          if (this[i].FirstName == fName)
            return this[i]; //return the Person

        return null;  //Person not Found!
      }
    }

    //Methods:

    public void Add(Person newPerson)
    {
      pArr.Add(newPerson);
    }

    public void Remove(Person p)
    {
      pArr.Remove(p);
    }

    public void RemoveAt(int idx)
    {
      pArr.RemoveAt(idx);
    }

    public string PrintAll()
    {
      string str = "";

      for (int i = 0; i < Count; i++)
      {
        //str+=((Person)pArr[i]).Print();
        //Same as:
        str += this[i].Print() + "\n";
      }

      return str;
    }

    public double GetSumSalary()
    {
      double sum = 0;

      for (int i = 0; i < Count; i++)
      {
        if (this[i] is Employee)
          sum += ((Employee)this[i]).Salary;
      }

      return sum;
    }


    #region IEnumerable Members

    public IEnumerator GetEnumerator()
    {
      return pArr.GetEnumerator();
    }

    #endregion
  }
}

using System;

namespace CustomCollections
{
  class Program
  {
    static void Main(string[] args)
    {
      PersonCollection pcol = new PersonCollection()
      {
        new Person("A","AA"),
        new Employee("B", "BB",10000),
        new Student("C", "CC",new int[]{80,100,90}),
        new Person("D", "DD"),
        new Manager("E", "EE",20000,5000)
      };

      //using indexer (set):
      pcol[3] = new Person("Lior", "Zamir");

      //using indexer (get):
      Console.WriteLine("** The fourth person:"
                        + pcol["Lior"].Print());

      Console.WriteLine("\n** Printing all persons:\n"
                        + pcol.PrintAll());

      Console.WriteLine("\n** Sum Salary (Employees only):\n"
                        + pcol.GetSumSalary());
    }
  }
}

פלט התוכנית:

indexers - פלט





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:04 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: ממשקים - Interfaces

interface (ממשק) הינו מבנה לוגי מופשט (אבסטרקטי) המכיל רק הצהרות.

אחד מתפקידי ה- interface העיקריים הוא ליצור ממשק זהה לאובייקטים שונים (אפילו בהיררכיות שונות) ובכך הוא אחד היישומים של עיקרון הפולימורפיזם בתכנות מונחה העצמים (OOP).


דוגמא ל- interface:

  interface IPrint
  {
    string Print();
  }

דגשים חשובים לגבי interface

  • אינו יכול להכיל משתנים או מימושים (פונקציות עם תוכן) אלא רק הצהרות. המחלקות/מבנים שירשו את ה- interface הם אלה שיממשו את ההצהרות.
  • כאשר נממש (נירש) interface חובה לממש את כל ההצהרות שבו
  • ניתן לרשת רק מחלקה אחת ובנוסף ניתן לרשת ולממש מספר בלתי מוגבל של interfaces
  • לא ניתן ליצור אובייקט מ- interface אלא רק ייחוס (reference)
  • גם struct יכול לרשת מ- interface
  • נהוג ששם של interface יתחיל באות I


דוגמא למימוש interface:


  class Person : IPrint
  {
    public string Print()
    {
      return "Hello";
    }
  }

דוגמא לשימוש ב- interface:

   IPrint p = new Person();
   Console.WriteLine(p.Print());

Interface הינו למעשה חוזה בין מחלקות המאפשר למתכנת לבנות תבנית ולבנות שירות המבוסס על תבנית זו וכל אחד שיממש את התבנית יוכל להשתמש בשירות זה.

בסביבת ה- NET. יש מספר רב של interface המאפשרים להשתמש בשירותים שונים הקיימים בסביבה. להלן דוגמא לכמה מהם:

  • IComparable
  • IComparer
  • IEnumerable
  • IEnumerator

ניקח כדוגמא את הממשק IComparable המספק פונקציה אחת להשוואה בין אובייקטים:

  public interface IComparable
  {
    int CompareTo(object obj);
  }

להלן התיעוד של הפונקציה CompareTo המסביר מה צריך להיות הערך החוזר:

//The return value has these meanings:
//Value Meaning Less than zero This instance is less than obj.
//Zero This instance is equal to obj.
//Greater than zero This instance is greater than obj.

השימוש ב- interface מאפשר להשתמש בשירותים ב- .NET הדורשים השוואה כגון מיון. אם נרצה לאפשר יכולת כזו למחלקה שלנו נצטרך לממש את ה- interface לפי הקווים המנחים המופיעים בתיעוד.

לדוגמא נבנה מחלקה Person ונבצע את ההשוואה לפי הגיל:


  public class Person : IComparable
  {
    public int Age { get; set; }

    public string Print()
    {
      return string.Format("Age: {0}", Age);
    }

    public int CompareTo(object obj)
    {
      Person p = (Person)obj;

      if (Age < p.Age)
        return -1;

      if (Age > p.Age)
        return 1;

      return 0;
    }
  }

עכשיו ניתן להשתמש בשירות Sort הקיים במחלקה Array אשר משתמש ב- interface כדי לבצע את ההשוואה:


 class Program
  {
    static void Main(string[] args)
    {
      Person[] persons = new Person[]
      {
        new Person() { Age = 25 },
        new Person() { Age = 17 },
        new Person() { Age = 30 }
      };

      Console.WriteLine("Before Sorting:");
      foreach (Person p in persons)
        Console.WriteLine(p.Print());

      Array.Sort(persons);

      Console.WriteLine("\nAfter Sorting:");
      foreach (Person p in persons)
        Console.WriteLine(p.Print());
    }
  }

בפלט ניתן לראות את מצב המערך לפני ואחרי המיון:


Interfaces - פלט לפני ואחרי המיון





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:05 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: הרשאות גישה

להלן טבלה המסכמת את הרשאות הגישה ב- #C:



רמת נגישות

המשמעות

האם חל עלnamespaces ?

האם חל עלTypes ?

(enum, struct, class, interface, delegate)

האם חל עלmembers ?

(variable, const, readonly, property, method, event)

private

("פרטי")

הגישה מוגבלת לסוג (type) המכיל בלבד.

לא

לא

כן

(ברירת מחדל ל- members שבתוך class או struct)

protected

("מוגן")

הגישה מוגבלת למחלקה (class) המכילהולמחלקות היורשות אותה.

לא

לא

כן

internalprotected

("פנימי-מוגן")

הגישה מוגבלת לפרויקט (assembly) הנוכחי או למחלקות היורשות את המחלקה (גם אם הם בפרויקט אחר).

לא

לא

כן

internal

("פנימי")

הגישה מוגבלת לפרויקט (assembly) הנוכחי בלבד.

לא

כן

(ברירת מחדל)

כן

public

("ציבורי")

גישה לא מוגבלת.

כן

(ברירת מחדל)

כן

כן

(ל- members שבתוך enum או interface זוהיברירת מחדל ולא ניתן להגדיר אחרת)





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:05 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: מחלקה אבסטרקטית

מחלקה אבסטרקטית הינה מחלקה אשר מהווה בסיס למחלקות אחרות, אך אין לנו באמת צורך באובייקטים ממנה בתוכנית.

  • בכדי ליצור מחלקה אבסטרקטית יש לכתוב abstract לפני שם ה- class
  • לא ניתן ליצור אובייקטים ממחלקה אבסטרקטית
  • ניתן ליצור ייחוס ממחלקה אבסטרקטית (בד"כ לצורכי פולימורפיזם)
  • מחלקה אבסטרקטית יכולה להכיל כל דבר שמחלקה רגילה יכולה להכיל
  • מחלקה אבסטרקטית יכולה (אבל לא חייבת) להכיל פונקציות אבסטרקטיות
  • לא ניתן לכתוב פונקציה אבסטרקטית במחלקה שהיא לא אבסטרקטית
  • פונקציה אבסטרקטית היא פונקציה שאין לה מימוש. היא נכתבת כהצהרה ללא גוף (כלומר ללא בלוק של פקודות)
  • כאשר יורשים מחלקה אבסטרקטית חובה לממש את כל הפונקציות האבסטרקטית שבה (אם יש)
  • המימוש של פונקציה אבסטרקטית הוא באמצעות מילת המפתח override

להלן דוגמא ליצירת מחלקה אבסטרקטית עם פונקציה אבסטרקטית:


  abstract class Shape
  {
    public abstract double GetArea();
  }

  class Circle : Shape
  {
    public double Radius { get; set; }

    public override double GetArea()
    {
      return Math.PI * Radius * Radius;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      Shape b = new Circle() { Radius = 5 };
      Console.WriteLine(b.GetArea());
    }
  }


השוואה בין virtual ל- abstract

 

abstract

virtual

מימוש הפונקציה במחלקה

אין מימוש של הפונקציה - לא צריך להגדיר את גוף הפונקציה, רק כותרת.

יש לממש את הפונקציה (בדומה לפונקציה רגילה). משמש לפולימורפיזם.

מימוש הפונקציה במחלקות היורשות

חובה לממש במחלקות היורשות בצורה ישירה.

לא חובה לממש.

מיקום הפונקציה

במחלקה אבסטרקטית בלבד

בכל מחלקה

הצהרה

לא private (משום שאז הפונקציה לא תיראה במחלקות הנגזרות ולא נוכל לממש אותה)





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:07 לינק ישיר 

אני מבקשת לא להוסיף לאשכול זה תגובות נוספות. אם יש הצעות ייעול - אשמח לקבל בפרטי.



דדווח על תוכן פוגעני

סמל אישי
מנותק
   
בית > פורומים > אינטרנט ומחשבים > למתכנתים שבינינו > מדריך C# - שני
מנהל לחץ כאן לנעילת האשכול
הוסף לעמוד האישי  דווח למנהל שלח לחבר

bholext