‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬

‫סיכום קורס – ‪ CSharp‬בסיסי‪.‬‬

‫על ידי שלמה גולדברג )הרב דוטנט(‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫תוכן העניינים‪:‬‬ ‫•‬

‫הקדמה ל – ‪Microsoft.net‬‬

‫•‬

‫היכרות עם ה – ‪Type System‬‬

‫•‬

‫עבודה עם מחלקות‬

‫•‬

‫מנגנון ניקוי הזיכרון‪.‬‬

‫•‬

‫מערכים ו – ‪.List‬‬

‫•‬

‫עבודה עם מחרוזות‪.‬‬

‫•‬

‫‪Object Oriented‬‬

‫•‬

‫מבנים‬

‫•‬

‫‪nullable‬‬

‫•‬

‫‪enums‬‬

‫•‬

‫טיפול בשגיאות‬

‫•‬

‫‪Operator Overload‬‬

‫•‬

‫‪Reflection‬‬

‫•‬

‫‪Attributes‬‬

‫•‬

‫ממשקים‪.‬‬

‫•‬

‫‪Delegates and events‬‬

‫הקדמה ל – ‪.Microsoft net framework‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫‪ Microsoft net framework‬באה לאפשר לנו כמתכנתים לכתוב קוד בכל השפות‬ ‫)התומכות ‪ (net‬עבור כל הסביבות )‪ (web, win, selular‬במגוון טכנולוגיות )עבודה עם‬ ‫קבצים‪ ,‬קישור לבסיס נתונים ועוד( והכול מתוך סביבה אחת ללא תלות במנגנונים‬ ‫חיצוניים‪.‬‬ ‫‪ Microsoft net framework‬מחולקת לשני חלקים עיקריים‪:‬‬ ‫•‬

‫‪ – FCL‬המוכר בשם ‪Framework Class Library‬‬

‫•‬

‫‪ – CLR‬המוכר בשם ‪Common Language Runtime‬‬

‫ה – ‪ FCL‬הינו ספרייה גדולה מאוד של מאות ואלפי פונקציות מובנות שנוכל להשתמש‬ ‫בה מבלי שנצרך לכתוב את הקוד לבד )לדוגמא‪ ,‬אם נרצה להצפין מחרוזת‪ ,‬לא נצטרך‬ ‫לכתוב את כל האלגוריתם של הצפנה ופיענוח‪ ,‬אלא נוכל להשתמש בקוד מוכן(‪.‬‬ ‫ה – ‪ CLR‬לעומת זאת זה המנוע העיקרי של פיתוח קוד בטכנולוגיית ‪ ,net‬הוא זה‬ ‫שאחראי להריץ את הקוד‪ ,‬לזרוק או לטפל בשגיאות לנקות את הזיכרון ועוד‪.‬‬ ‫כדי שנוכל להריץ קוד שנכתב ב – ‪ ,net‬נצטרך לוודא שעל מחשב הלקוח מותקנת‬ ‫גרסת ה – ‪ framework‬המתאימה שבעזרתה פיתחנו את המוצר‪.‬‬

‫קומפילציה וזמן ריצה‪.‬‬ ‫בשפות אחרות‪ ,‬תהליך הקומפילציה לוקח את הקוד שכתבנו ומקמפל אותו לשפת‬ ‫מכונה‪ ,‬כך שיש בקובץ קוד בינארי שניתן להבנה על ידי החומרה‪ ,‬בשפות ‪ net‬לעומת‬ ‫זאת הקומפילציה בסך הכול ממירה את הקוד מהשפה בה כתבנו )‪C#, VB.NET, F#‬‬ ‫ועוד( לשפת ביניים של ‪ ,net‬מוכרת בשם ‪.IL‬‬ ‫בזמן ריצה הקוד נטען לזיכרון והפונקציה הראשונה מתקפלת לשפת מכונה )בעזרת‬ ‫תהליך שנקרא ‪ ,(JIT‬הקוד בפנים יבוצע‪ ,‬במידה שבפנים יש קריאות לפונקציות אחרות‬ ‫)בין אם זה קוד שכתבתנו ובין אם זה קוד של ה – ‪ FCL‬למשל( אותם פונקציות יקומפלו‬ ‫לפני הביצוע שלהם לשפת מכונה‪) .‬זה הסיבה דרך אגב שבזמן מדידות ביצועים נתעלם‬ ‫מהקריאה הראשונה של הפונקציה מכיוון שהיא לוקחת קצת יותר זמן )הזמן של‬ ‫ההידור משפת ‪ IL‬לשפת מכונה(‪ ,‬אולי זה נראה קצת פוגע בביצועים‪ ,‬אבל לעומת זאת‬ ‫היכולת של השפה להעביר את הקוד לשפת מכונה מותאם אישית לפי הסביבה בה‬ ‫היא רצה‪ ,‬נותן הרבה מאוד יכולות מאשר הידור רגיל למכנה המשותף הכי נמוך של‬ ‫כל המעבדים והסביבות‪.‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬

‫שפות ו ‪Types -‬‬ ‫אחת מהיכולות של ‪ ,net‬זה היכולת לכתוב בשפה אחת ולהשתמש בקוד משפה‬ ‫אחרת‪ ,‬הסיבה שניתן לעשות זאת )בשונה משפות אחרות( היא במושג ‪– CTS‬‬ ‫)‪ ,(Common Type System‬למעשה בכל השפות של סביבת ‪ ,net‬כל ה – ‪ types‬הם‬ ‫אותו דבר )‪ int‬בשפת ‪ C#‬ו – ‪ Integer‬בשפת ‪ ,vb‬זה למעשה ‪.(Int32‬‬ ‫לאחר הקומפילציה כל הקוד שאנחנו כותבים הופך ב – ‪ IL‬ל – ‪ types‬המשותפים‪ ,‬ברגע‬ ‫שכל השפות מדברים באותם ה – ‪ ,types‬אין בעיה לכתוב קוד בשפה אחת ולהשתמש‬ ‫בשפה אחרת‪.‬‬

‫היכרות עם ה – ‪Type System‬‬ ‫ישנם שני סוגים של ‪ Types‬עיקריים‪ ,‬האחד נקרא ‪ Value Type‬והשני נקרא ‪Reference‬‬ ‫‪ ,Type‬להלן רשימת הסוגים‪:‬‬ ‫•‬

‫‪ – Value Type‬הם כל הסוגים הפרימיטיביים )מספרים‪ ,‬כן‪/‬לא וכו'(‪ ,‬כל מי שהוא‬ ‫מסוג ‪ Struct‬וכל מי שהוא מסוג ‪.enum‬‬

‫•‬

‫‪ – Reference Type‬כל מי שהוא מסוג ‪ ,class‬וכל מי שהוא מסוג מערך )כולל‬ ‫מערכים של סוגים פרימיטיביים‪.‬‬

‫ההבדל הטכני בין שני הסוגים‪ ,‬זה היכן זה יושב בזיכרון‪ ,‬המשתנים מסוג ‪Value Type‬‬ ‫מוגדרים ב – ‪ Stack‬לעומת המשתנים מסוג ‪ Reference Type‬יושבים ב – ‪.Heap‬‬ ‫ההבדל המעשי ביניהם זה בהשמה ובהשוואה‪ ,‬לדוגמה‪ ,‬נניח שיש לנו משתנה מסוג ‪int‬‬ ‫בשם ‪ ,i1‬שיש בו את הערך ‪ ,5‬ונכתוב שורת קוד המגדירה משתנה חדש מסוג ‪ int‬בשם‬ ‫‪ ,i2‬שנציב בו את הערך שיש ב – ‪ (int i2 = i1) .i1‬כעת יהיה כמובן גם במשתנה ‪ i2‬את‬ ‫הערך ‪ ,5‬מה יקרה במידה ונשנה את אחד מהמשתנים‪ ,‬כמובן שזה ישנה רק אותו‪ ,‬לדוגמה‬ ‫אם נשנה את המשתנה ‪ ,i1‬זה לא ישנה בכלל את הערך של המשתנה ‪.i2‬‬ ‫לעומת זאת במידה את אותו סיפור רק עם משתנים מסוג ‪) ,Reference Type‬אובייקט מסוג‬ ‫‪ ,(Person‬שיש לו משתנה מסוג מספר בשם ‪ age‬עם הערך ‪ ,30‬וכעת נגדיר משתנה חדש‬ ‫מסוג ‪ Person‬ונציב בו את הראשון )‪ ,(Person p1 = p2‬שינוי של הגיל של אחד ישנה מידית‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫גם את השני )‪ (p1.age = 90‬יגרום גם ל – ‪ p2.age‬לקבל את הערך החדש‪ ,‬מכיוון ששניהם‬ ‫מצביעים לאותו מקום בזיכרון‪.‬‬ ‫אותו דבר יקרה גם בהשוואה‪ ,‬השוואה של שני משתנים מסוג ‪ VT‬משווה את הערך האמתי‬ ‫שלהם‪ ,‬לעומת השוואה של שני משתנים מסוג ‪ ,RT‬ישווה את הכתובת של שני המשתנים‪,‬‬ ‫כלומר בדיקה האם ‪ p1‬שווה ל – ‪ p2‬ייתן את התוצאה "כן"‪ ,‬לעומת השוואה למשתנה חדש‬ ‫בשם ‪ ,(p3 = new Person) p3‬גם הערך ב – ‪ age‬יהיה ‪) 90‬כמו ב – ‪ (p1‬עדיין יחזיר "לא"‪,‬‬ ‫מכיוון שמדובר בשני אובייקטים שונים‪.‬‬

‫המרה בין סוגי משתנים‬ ‫יש כמה סוגי המרות‪ ,‬ההמרה הבסיסית נקראת ‪ ,cast‬זהו התחזות של אובייקט מסוג אחד‬ ‫לאחר‪ ,‬לדוגמה אם יש לנו משתנה מסוג ‪ long‬שיש בו ערך כלשהו‪ ,‬ונרצה להגדיר משתנה‬ ‫מסוג ‪ int‬ולהציב את הערך של המשתנה מסוג ‪ ,long‬זה יכול להוות בעיה‪ ,‬מכיוון שייתכן‬ ‫שבמשתנה מסוג ‪ long‬יש ערך גדול יותר מאשר יכול להיכנס ב – ‪ ,int‬ולכן נאלץ לבצע‬ ‫‪ ,(int i1 = (int)theLongNum) ,cast‬במקרה זה אנחנו לוקחים את האחריות על עצמנו‪,‬‬ ‫תהליך ה – ‪ cast‬יכול לעבוד רק על משתנים שיש ביניהם הגדרת תהליך המרה‪.‬‬ ‫לפעמים נרצה להמיר בין סוגים ללא המרה‪ ,‬עבור המרת מחרוזות למספרים יש את‬ ‫פונקציית ‪) xx.Parse‬כש – ‪ xx‬מתייחס לסוג )לדוגמה – ‪ (int.parse‬וכן הלאה(‪ ,‬פונקציה זו‬ ‫יודעת לקבל מחרוזת ולהחזיר מספר‪ ,‬אופציה נוספת היא פונקציות ה – ‪.Convert.ToXX‬‬

‫‪Boxing and unboxing‬‬ ‫במידה ונציב משתנה מסוג ‪ VT‬לתוך משתנה מסוג ‪) object‬וזה חוקי‪ ,‬כפי שנלמד בנושא‬ ‫של ‪) Object Oriented‬שלאבא מותר להצביע על בן((‪ ,‬יקרה תהליך שנקרא ‪ ,boxing‬מכיוון‬ ‫שכפי שציינו זה חוקי‪ ,‬אך מצד שני ‪ VT‬אמור לשבת ב – ‪ Stack‬לעומת ‪ RT‬שאמורים לשבת‬ ‫ב – ‪.Heap‬‬ ‫תהליך ה – ‪ ,boxing‬מעתיק את המידע לזיכרון המתאים )‪ (heap‬אבל מציין במפורש את‬ ‫סוג המשתנה המועתק‪ ,‬בתהליך ה – ‪ (int I = (int)obj) unboxing‬צריך לציין מה המידע‬ ‫שאנחנו מוציאים‪ ,‬התחביר נראה די דומה ל – ‪ ,cast‬אבל הוא מטעה‪ ,‬מכיוון ש – ‪ cast‬הינו‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫המרה‪ ,‬ואם יש ב – ‪ heap‬כרגע מספר מסוג ‪ ,int‬וננסה להוציא החוצה למספר מסוג ‪long‬‬ ‫)אע"פ שאין בעיה בגודל( זה יתרסק‪.‬‬

‫השוואה בין אובייקטים‬ ‫ניתן להשוות בין אובייקטים באחד מהאופציות הבאות‪:‬‬ ‫•‬

‫האופרטורים ==‪ - =! ,‬משוה עבור ‪ VT‬את התוכן‪ ,‬ועבור ‪ RT‬את הכתובת‪.‬‬

‫•‬

‫מתודת ‪ Equal‬של האובייקט )מגיעה מ – ‪ (object‬אותו דבר כמו האופרטור )אלא‬ ‫אם כן הוגדר אחרת(‬

‫•‬

‫מתודת ‪ Equal‬סטטית שמקבלת שני פרמטרים‪ ,‬מפעילה את ה – ‪ Equal‬הרגיל של‬ ‫הפרמטר הראשון‪ ,‬אך מוודא שהוא לא ‪.null‬‬

‫•‬

‫מתודת ‪ ReferenceEqual‬תמיד משווה כתובות‪.‬‬

‫עבודה עם מחלקות‬ ‫כדי להתחיל לעבוד עם מחלקות‪ ,‬עלינו להכיר מספר אלמנטים‪ ,‬הראשון בהם נקרא ‪Access‬‬ ‫‪ .Modifier‬ישנם חמשה אופציות‪:‬‬ ‫•‬

‫‪ – private‬שאומר שהאלמנט יהיה פרטי ונגיש רק למחלקה בה זה הוגדר‪.‬‬

‫•‬

‫‪ - public‬שאומר שהאלמנט יהיה נגיש מכל מקום‪.‬‬

‫•‬

‫‪ – protected‬רק מי שיורש מהאלמנט‪.‬‬

‫•‬

‫‪ – internal‬רק אלמנטים בתוך ה – ‪.dll‬‬

‫•‬

‫‪ – protected internal‬או מי שיורש או מי שמוגדר בתוך ה – ‪.dll‬‬

‫בתוך מחלקה יכול להיות הרבה מאוד אלמנטים‪.‬‬ ‫•‬

‫משתנים‪.‬‬

‫•‬

‫פונקציות‬

‫•‬

‫בנאים )‪(constructor‬‬

‫•‬

‫מאפיינים‬

‫•‬

‫מידע סטטי‪.‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫נעבור על כל אחד מהאלמנטים הללו‪ ,‬ונסביר את המשמעות שלהם‪.‬‬ ‫משתנים‪:‬‬ ‫כל מחלקה יכולה להכיל משתנים שזהו למעשה המידע שיש למחלקה‪ ,‬לדוגמא – למחלקת‬ ‫"אדם" יכול להיות משתנים שיחזיקו את המידע על הגיל‪ ,‬השם או גובה‪ ,‬וכד'‪.‬‬ ‫למשתנים של מחלקה יש לי ערך ברירת מחדל‪ ,‬כלומר לא צריך לאתחל אותם לפני‬ ‫שמשתמשים בהם )בשונה ממשתנים שמוגדרים בתוך פונקציות שנקבל שגיאת‬ ‫קומפילציה‪ ,‬אם נשתמש בהם לפני אתחול(‪.‬‬ ‫פונקציות‬ ‫בפונקציות נשתמש בדרך כלל כשנרצה לעשות פעולות כלשהם על המחלקה כשבדרך כלל‬ ‫הפעולות יכללו שימוש או שינוי של המידע הקיים במשתנים‪.‬‬ ‫בנאים‬ ‫כמפתחים לפעמים נרצה לשלוט בזמן יצירת האובייקט בקוד שייווצר‪ ,‬ולכן נוכל להגדיר‬ ‫בנאי‪ ,‬הבנאי הוא מתודה מיוחדת ששמה כשם המחלקה ואין לה הגדרה של ערך מוחזר‬ ‫)גם לא ‪ ,(void‬בזמן שמישהו יבצע פעולת ‪ new‬על האובייקט מתודת הבנאי תופעל ונוכל‬ ‫לאתחל את המשתנים שלנו‪.‬‬ ‫מאפיינים‬ ‫בדרך כלל משתנים יוגדר כ – ‪ ,private‬במידה וכך לא נוכל לגשת למשתנים מבחוץ‪ ,‬הדרך‬ ‫לאפשר בכל זאת קריאה או כתיבה‪ ,‬נוכל להגדיר מאפיינים‪ ,‬מאפיין למעשה הינו מתודה‬ ‫שנכתבת בתחביר מיוחד )‪ (get, set‬שמאפשרת לנו להשתמש בפונקציות הללו בצורה‬ ‫כאילו הם משתנים‪ ,‬כדי להקל עלינו את הכתיבה )בעיקר במקרים בהם אין לנו שום לוגיקה‬ ‫לבצע( נוכל לכתוב ‪) Automatic Properties‬מאפיינים ללא לוגיקה(‪.‬‬ ‫סטטי‬ ‫מידע סטטי הינו כזה שהוא לא קשור למופע מסוים של מחלקה אלא להגדרת כל האובייקט‬ ‫)למשל מידע על שכר המינימום‪ ,‬אינו קשור לעובד כזה או אחר‪ ,‬אלא להגדרת העובד‬ ‫בכללותו(‪ ,‬כמו כן ניתן להגדיר פונקציות סטטיות שתפקידם לטפל באותם משתנים‪,‬‬ ‫לפונקציות סטטיות אין דרך לגשת למשתנים רגילים‪ ,‬מכיוון שאין להם גישה למילה ‪.this‬‬ ‫כדי לאתחל משתנים סטטיים נוכל להגיר בנאי סטטי‪ ,‬שיופעל אוטומטית על יד ה – ‪,clr‬‬ ‫בזמן שייעשה פנייה לאלמנט כלשהו של המחלקה‪ ,‬לבנאי סטטי אי אפשר לשלוח‬

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

‫מנגנון ניכוי הזיכרון‬ ‫מנגנון ה – ‪ ,GC‬מטפל בכל מה שקשור לניכוי הזיכרון שאנחנו מקצים‪ ,‬בכל פעם שניצור‬ ‫מופע של מחלקה‪ ,‬ייווצר אובייקט כלשהו ב – ‪ ,heap‬מישהו צריך לנקות אותו משם‬ ‫כאשר הוא הופך להיות זבל‪ ,‬ההגדרה של משתנה זבל הינו כאשר אי אפשר להגיע‬ ‫אליו‪ ,‬לדוגמה‪ ,‬הגדרת משתנה )מסוג ‪ (Reference‬בפונקציה‪ ,‬לאחר היציאה‬ ‫מהפונקציה האובייקט נשאר בדד ב – ‪.heap‬‬ ‫מתי שהוא‪ ,‬כשאין ל – ‪ clr‬מה לעשות‪ ,‬או שהמקום בזיכרון התמלא‪ ,‬ה – ‪ GC‬נכנס‬ ‫לפעולה‪ ,‬הוא מתחיל לסמן מיהם האובייקטים אותם צריך לנקות‪ ,‬כאשר הוא מסיים‬ ‫לסמן‪ ,‬הוא עוצר את פעולת התוכנית‪ ,‬ומוחק את אובייקט הזבל‪ ,‬כדי למנוע בעיית‬ ‫פרגמנטציה )כלומר – חורים בזיכרון( ה – ‪ GC‬מצמצם את כל הזיכרון הפנוי‪ ,‬כך שכל‬ ‫האובייקטים אשר עדיין בשימוש‪ ,‬יהיו אחד ליד השני‪ ,‬פעולה גורמת לעדכון המצביעים‬ ‫ב – ‪ ,stack‬ולכן יש צורך לעצור את פעולת התוכנית‪.‬‬ ‫כדי לשפר ביצועים‪ ,‬ה – ‪ GC‬מחלק את הזיכרון לשלוש דורות‪ ,‬כאשר הוא מבצע ניקוי‬ ‫על הדור הראשון‪ ,‬הוא מעביר את יתרת האובייקטים ששרדו לדור השני‪ ,‬כעת הניקויים‬ ‫הבאים של ה – ‪ GC‬יתמקדו רק בדור הראשון‪ ,‬ורק כאשר הדור השני יתמלא )מכיוון‬ ‫שעברו מספר סיבובים על הדור הראשון( הוא ינקה את הדור השני ויעביר את היתרה‬ ‫לדור השלישי‪ ,‬כך הוא מוודא שלא יבדקו לשווא אובייקטים שכבר שרדו סיבוב אחד או‬ ‫יותר‪.‬‬ ‫לפעמים אובייקטים צריכים להגדיר ‪) dtor‬מפרק – כמו ‪ ,ctor‬רק עם ~ בשם של‬ ‫המתודה(‪ ,‬במקרים אלו ה – ‪ GC‬אמור להריץ את הקוד שלהם בזמן הניקוי )למשל‬ ‫סגירה של קובץ וכדו'( כדי למנוע את עצירת הקוד לזמן בלתי צפוי‪ ,‬הוא מכניס את‬ ‫אותם אובייקטים שיש להם מפרק לתור מיוחד‪ ,‬שלאחר מכן הוא יריץ את קוד הניקוי‬ ‫שלהם בתהליך נפרד‪ ,‬ההמלצה היא להגיר מתודה בשם ‪) Dispose‬מומלץ להגדיר על‬ ‫המחלקה שהיא מממשת את ‪ – IDisposable‬נלמד על ממשקים מאוחר יותר(‪ ,‬לכתוב‬ ‫את קוד הניקוי‪ ,‬בפנים – ולקוות שהמתכנת יזכור להפעיל את המתודה‪ ,‬בלי קשר‬ ‫לכתוב מפרק ולקרוא למתודת ה – ‪ ,Dispose‬וכדי לוודא שהמתודה לא תופעל פעמיים‪,‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫יש להוסיף במתודת ה – ‪ Dispose‬הגדרה ל – ‪ GC‬לא להפעיל אותה שוב‬ ‫)‪(GC.Suppress‬‬

‫מערכים ו – ‪.List‬‬ ‫מערכים הינם הגדרה רצופה של אובייקטים מאותו סוג‪ ,‬כל מי שהוא מסוג מערך יושב‬ ‫על ה – ‪ ,heap‬כל המערכים יורשים ממחלקה בשם ‪ ,Array‬שיש לה גם מתודות סטטיות‬ ‫שניתן להיעזר בהם לעבודה עם מערכים )כמו למשל ‪ ,Array.Copy‬שמאפשר להעתיק‬ ‫מערך אחד לשני(‪.‬‬ ‫הבעיה עם מערכים היא שצריך להגדיר מראש את גודלם‪ ,‬כדי להתמודד עם זה יש‬ ‫אובייקט מיוחד בשם ‪) List‬שמוגדר ‪ ,Generic‬מכיוון שצריך להגיר את הסוג בין שני‬ ‫סוגרים >‪ List
‫מחרוזות‬ ‫משתנה מסוג ‪ string‬הוא משתנה מיוחד‪ ,‬מצד הוא הוא ‪ ,class‬כך שהוא מוגדר כ –‬ ‫‪ ,Reference type‬מצד שני העבודה אתו מאוד דומה למשתנים רגילים )ואפילו אפשר‬ ‫להגדיר אותו כ – ‪.(const‬‬ ‫כל מניפולציה על מחרוזת )=‪ Remove, Replace, +‬ועוד( יוצרת מחרוזת חדשה‪ ,‬ואי‬ ‫אפשר לשנות את המקור‪ ,‬ולכן כשיש קצת יותר ממעט מניפולציות על מחרוזת אחת‪,‬‬ ‫מומלץ להשתמש ב – ‪ ,StringBuilder‬כדי לשפר את ביצועי המערכת )הרבה‬ ‫מניפולציות על מחרוזות‪ ,‬יגרום להרבה זבל בזיכרון )כל שינוי על המחרוזת זורק את‬ ‫הקודמת ומגדיר אחד חדשה( מה שאומר כמובן הרבה קריאות של ה – ‪(GC‬‬

‫‪Object Oriented‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫‪ C#‬הינה שפה המוגדרת כ – ‪ ,Object Oriented‬זה אומר שלכל אובייקט במערכת יש‬ ‫אבא כלשהו‪ ,‬האבא הראשי של כל האובייקטים נקרא ‪ ,object‬כולם יורשים ממנו והוא‬ ‫מדיר מספר פונקציות כלליות שכולם מקבלים )‪ ToString, Equal‬ועוד(‪.‬‬ ‫כדי לרשת מאובייקט כלשהו‪ ,‬נשתמש בסימן ‪) :‬נקודתיים( לדוגמא – ‪Employee :‬‬ ‫‪ ,Person‬בהנחה שיש אובייקט בשם ‪ Person‬שמכיל מידע ויכולות כלשהם‪ ,‬הוגדר‬ ‫מחלקה בשם ‪ Employee‬שיורש אותו‪ ,‬ניתן להסתכל על המושג ירושה בתור הרחבה‪,‬‬ ‫מכיוון שאנחנו מקבלים את כל מה שיש אצל האבא‪ ,‬ומרחיבים אותו עם יכולות ומידע‬ ‫נוסף‪ ,‬בדוגמה שלנו‪ ,‬לאובייקט ‪ Person‬יש יכולות ומידע על אדם‪ ,‬אך לאובייקט‬ ‫‪ Employee‬יש מידע נוסף כגון השכר‪ ,‬שנות וותק ועוד‪.‬‬ ‫לפעמים אצל האבא יוגדר מתודה )או מאפיין( שהוא ירצה לאפשר לבן לדרוס אותה‬ ‫)כלומר לשנות את המימוש שלה( כדי לעשות זאת הוא יכול לכתוב את המילה ‪virtual‬‬ ‫בהגדרת המתודה‪ ,‬הבן לעומת זאת כשהוא ירצה לשנות את המימוש יצטרך להשתמש‬ ‫במילה ‪ ,override‬כשזה יקרה‪ ,‬במידה ויפעילו את המתודה )גם אם המצביע הוא של‬ ‫האבא – לדוגמא‪ (Person p = new employee ,‬המתודה שתופעל תהיה של הבן )זהו‬ ‫פולימורפיזם(‪.‬‬ ‫ישנם מקרים בהם רוצים להגדיר אבא שהינו אבסטרקטי – כלומר‪ ,‬רעיון כגון "צורה"‬ ‫אין משמעות לצורה‪ ,‬יש משמעות לריבוע‪ ,‬עיגול וכדו'‪ ,‬ניתן להגדיר אובייקט אבסטרקטי‬ ‫בעזרת המילה ‪ ,abstract‬כך שלא ניתן יהיה ליצור מופע מהאובייקט‪ ,‬אך הוא יוכל‬ ‫לשמש אבא למי שיירש ממנו )כך ניתן יהיה למשל להגדיר מערך של צורות( ובנוסף‬ ‫האבא יכול להגדיר מתודות )בעזרת ‪ (abstract‬שהבן חייב לממש אותם‪.‬‬ ‫במידה והמצביע הינו של האבא‪ ,‬בזמן פיתוח אי אפשר לקבל ברשימה את המתודות‬ ‫של הבן‪ ,‬ישנם שלושה דרכים כדי לקבל‪:‬‬ ‫•‬

‫‪ – cast‬המרה רגילה‪ ,‬תתרסק אם הבן אינו האובייקט האמתי שאנחנו מנסים‬ ‫להמיר‪.‬‬

‫•‬

‫‪ – is‬מאפשר לנו לבדוק‪ ,‬האם אובייקט הוא מסוג מסוים‪.‬‬

‫•‬

‫‪ – as‬המרה בטוחה‪ ,‬שתחזיר ‪ null‬במידה והאובייקט לא עבר המרה בהצלחה‪.‬‬

‫מבנים )‪(struct‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫מבנים מאוד דומים למחלקות )‪ ,(class‬רק שיצירת מופעים של מחלקות מושיבה אותם‬ ‫ב – ‪ heap‬וגורמת ל – ‪ GC‬לעבוד קשה יותר‪ ,‬לעומת יצירת מופעים של מבנים שמה‬ ‫אותם ב – ‪ stack‬וזה יעלם ברגע שנצא מהפונקציה‪.‬‬ ‫מבנים אמורים להיות קטנים )לא יותר מ – ‪ (16‬מכיוון שבכל העברה לפונקציה הם‬ ‫מועתקים )תחשבו על לולאה של אלף‪ ,‬שכל פעם קוראת לפונקציה ושולחת מבנה –‬ ‫המבנה יועתק המון פעמים בזיכרון(‪.‬‬ ‫אי אפשר לרשת ממבנים ומבנים אינם יכולים לרשת‪ ,‬תמיד יהיה להם בנאי ברירת‬ ‫מחדל )בשונה ממחלקה שיש לו ברירת מחדל‪ ,‬אבל אם כתבנו בנאי משלנו נצטרך‬ ‫ליצור אחד נוסף אם נרצה בנאי ברירת מחדל(‪.‬‬ ‫בכל הבנאים שנכתוב עלינו לאתחל את כל המשתנים המוגדרים במבנה‪ ,‬במידה ויהיה‬ ‫לנו ‪ ,Automatic properties‬נהיה חייבים להפעיל בבנאים שלנו את הבנאי ברירת‬ ‫המחדל‪.‬‬

‫‪nullable‬‬ ‫לפעמים גם עבור משתנים שהם ‪ Value Type‬נצטרך להכניס את הערך ‪ ,null‬לדוגמא‬ ‫– התשובה "כמה כסף יש למישהו בחשבון" יכולה להיות מספר כלשהו‪ ,‬אבל גם‬ ‫התשובה של "לא ידוע" הינה לגיטימית‪ ,‬כדי לתמוך בזה‪ ,‬יש אפשרות להגדיר ‪nullable‬‬ ‫‪ ,type‬ההגדרה היא בעזרת סימן השאלה )?‪ ,(bool? ,int‬משתנה מסוג זה יכול להכיל‬ ‫את הערך שלו או ‪.null‬‬ ‫אי אפשר לגשת למשתנה מסג זה בלי לוודא ראשית שיש לו ערך )אחרת התוכנה‬ ‫תתרסק( ניתן לשאול האם הוא שונה מ – ‪ ,null‬או להשתמש ב – ‪ ,HasValue‬כדי‬ ‫להוציא את הערך ניתן להשתמש ב – ‪.Value‬‬ ‫אם נרצה לקבל את הערך ואם אין כלום לקבל ערך ברירת מחדל ניתן להשתמש בצורה‬ ‫הבאה ))‪ (i.GetValueOrDeafult(10‬בהנחה שיש משתנה בשם ‪ ,i‬או בצורה הקצרה‬ ‫יותר )‪ ,(I ?? 10‬שני הצורות יחזירו את הערך של ‪ i‬או ‪ 10‬אם אין לו שום ערך‪.‬‬

‫‪.enums‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫ישנם מקרים בהם יש לנו רשימות שנרצה לתת למשתמש לבחור אחד מהם )לדוגמה‪,‬‬ ‫רשימת פירות‪ ,‬או רשימת צבעים( בקוד זה אומר להיות לרוב משתנה מסוג מספר או‬ ‫משתנה מסוג מחרוזת‪ ,‬לכל אחד מהם יש בעיה אחרת‪ ,‬שימוש במשתנה מסוג מספר‬ ‫הופך את הקוד לבלתי קריא וקשה לתחזוקה )בעוד שנה מי יזכור מה היה המשמעות‬ ‫של הערך ‪ ,(23‬לעומת זאת שימוש במחרוזות יכול להביא לבאגים אם מתכנת כלשהו‬ ‫יטעה בתו )וזאת תהיה שגיאה לוגית ולא שגיאת קומפילציה(‬ ‫כדי לפתור את הבעיה ניתן להגדיר ‪ ,enum‬המאפשר לנו לתת רשימה קבועה של‬ ‫שמות )ולכל אחד מהן יש ערך(‪ ,‬כך המצד אחד הקוד קריא ומצד שני לא יכולות להיות‬ ‫טעויות‪.‬‬ ‫היות שבסופו של דבר ‪ enum‬הם מספרים‪ ,‬המשתנים מסוג ‪ enum‬הם ‪Value Type‬‬ ‫ונשמרים ב – ‪.stack‬‬ ‫כדי לעבוד עם ‪ enum‬נוכל להשתמש במחלקה הסטטית ‪ Enum‬שיש לה מספר‬ ‫פונקציות )כמו ‪ GetNames‬שמחזירה מערך של מחרוזות עם כל השמות שיש‬ ‫ברשימה‪.‬‬ ‫במידה ונרצה שהמשתנה יחזיק יותר מערך אחד בו זמנית )למשל שני פירות(‪ ,‬נצטרך‬ ‫להגדיר שהערכים של ה – ‪ enum‬יהיו בחזקות של ‪) 2‬הראשון‪ ,1 :‬השני‪ ,2 :‬השלישי‪:‬‬ ‫‪ ,4‬הרביעי‪ 8 :‬וכן הלאה( כדי לוודא שלא תהיה שום קומבינציה של ערכים שמביאים‬ ‫את אותו מספר‪ ,‬כדי להציב משתנה שמחזיק יותר מערך ערך נכתוב בצורה הבאה‬ ‫)‪ ,(Color c = Color.Red | Color.Blue‬כדי לבדוק האם ‪ c‬מכיל צבע מסוים‪ ,‬נשתמש ב‬ ‫– ‪ ,HasFlag‬כדי שהדפסה של ‪ c‬תחזיר את הערך הנכון‪ ,‬נוכל להוסיף על ‪ enum‬את ה‬ ‫– ‪ attribute‬בשם ‪) Flags‬עוד על הנושא בהמשך(‬

‫טיפול בשגיאות‬ ‫במידה והקוד שנריץ יקרום לשגיאה יתעורר מצב חריג והתוכנית תתרסק‪ ,‬מצב חריג‬ ‫הינו כל מצב בו אנו לא יודעים או לא יכולים לטפל )חלוקה ב – ‪ ,0‬גישה למערך במקום‬ ‫שלא קיים‪ ,‬גישה למשתנה שיש בו ‪ null‬וכדו'(‪ ,‬המנגנון של הקרסת התוכנה‪ ,‬מוודא‬ ‫קודם האם ניסינו לטפל בבעיה‪ ,‬ניסיון לטפל בבעיה יגרום לכך שהתוכנה לא תתרסק‪.‬‬ ‫כדי לעשות זאת‪ ,‬נצטרך לעטוף את הקוד שיכול לזרוק שגיאה בבלוג של ‪– try, catch‬‬ ‫המנגנון עובד כך‪:‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬

‫•‬

‫הקוד הרגיל )בתוך בלוק ה – ‪ (try‬רץ‪:‬‬ ‫‪ o‬במידה והכול עבר בהצלחה‪ ,‬מדלגים על קטע ה – ‪ catch‬וממשיכים‬ ‫הלאה‪.‬‬ ‫‪ o‬במידה ונכשל‪ ,‬מדלגים על הקוד בבלוק ה – ‪ ,try‬לקוד בבלוק ה –‬ ‫‪ catch‬ומבצעים אותו‪ ,‬ומשם ממשיכים קדימה‪.‬‬

‫קוד ה – ‪ try‬יכול להיות בפונקציה שקוראת לפונקציה‪ ,‬וכל הקוד הפנימי לא ירוץ במידה‬ ‫ויש שגיאה‪.‬‬ ‫לא מומלץ לכתוב סתם קטעי קוד של תפיסת שגיאות כשכל המטרה היא רק להתעלם‬ ‫מהשגיאה‪ ,‬מכיוון שבצורה זו האפליקציה תבצע הרבה שגיאות לוגיות‪ ,‬מומלץ לתפוס‬ ‫את השגיאות )במקום גלובלי( ולתעד אותם‪ ,‬כדאי לכתוב בלוק ‪ ,try‬רק כשנרצה באמת‬ ‫להתעלם מהשגיאה )למשל‪ ,‬אם אנחנו שולחים מייל‪ ,‬ולא אכפת לנו שהמייל לא הגיע‬ ‫ליעדו( או שיש לנו קוד ספציפי שיכול לתקן את הבעיה )כמו למשל בקשה לשם קובץ‬ ‫חדש‪ ,‬אם שם הקובץ ששלחנו לא קיים(‪.‬‬

‫‪Operator Overload‬‬ ‫נושא זה מדבר על היכולת שלנו להגדיר כיצד יתנהגו אופרטורים על אובייקטים שלנו‪ ,‬ישנם‬ ‫שלושה סוגים שונים‪:‬‬ ‫•‬

‫כל האופרטורים )< > = ! ‪ ++‬וכד'(‬

‫•‬

‫אופרטור ‪) indexer‬סוגריים מרובעות ][(‬

‫•‬

‫‪) Casting‬הגדרת המרה בין סוגים שונים(‬

‫יש לכתוב קוד מסוג זה רק אם הוא ברור ואינטואיטיבי למתכנת‪ ,‬למשל אם אנחנו כותבים‬ ‫מחלקה שמייצגת מטבע‪ ,‬ונרצה לבדוק האם ‪ ,c1 > c2‬זה מאוד הגיוני לכתוב קוד של‬ ‫‪ Operator Overload‬של השוואה )זה יהיה פחות הגיוני על שני ‪ – Person‬אע"פ שאפשר‬ ‫להתייחס לגיל – אבל זה כבר לא אינטואיטיבי(‪.‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫הגדרת מתודה זו היא בעזרת הקוד הבא‪public static operator > (Currency c1, ) :‬‬ ‫‪.(Currency c2‬‬ ‫אופרטור ‪ ,indexer‬מאפשר לנו להתייחס לאובייקט שלנו כאילו הוא מערך‪ ,‬למשל אם נכתוב‬ ‫מחלקה בה יש ‪ private array‬ונרצה לחשוף מידע לפי המיקום של המערך‪ ,‬נוכל לעשות‬ ‫זאת בעזרת ‪ ,indexer‬התחביר הינו מאוד דומה למאפיינים‪ ,‬רק שבמקום שם של מאפיין‬ ‫נכתוב את המילה ]‪ ,this[int index‬או כל סוג של פרמטר שנרצה לקבל‪.‬‬ ‫‪ ,Casting‬מאפשר לנו לכתוב קוד שממיר אוטומטית בין סוגים שונים‪ ,‬למשל אם יש לנו‬ ‫מחלקת מטבע‪ ,‬ונרצה לכתוב קוד כזה )‪ (Currency c1 = 10‬זה לא ממש יעבוד אלא אם כן‬ ‫נגדיר המרה בין מספר למטבע‪ ,‬וזה יראה כך ) ‪public static implicit operator‬‬ ‫)‪ ,((Currency(int num‬ניתן להגדיר שההמרה מרומזת )‪ (implicit‬או מפורשת )‪(explicit‬‬ ‫ואז המתכנת יצטרך לכתוב ‪ ,Cast‬לדוגמה )‪.((Currency c = (Currency)10‬‬

‫‪Reflection‬‬ ‫אחד היכולות המדהימות של השפה היא היכולת שלנו לחקור את האלמנטים בזמן‬ ‫ריצה‪ ,‬חקירת האלמנטים נקראת ‪) Reflection‬השתקפות(‪ ,‬בדרך כלל הקוד שלנו‬ ‫מתייחס למאפיינים ולמתודות של המופעים שלנו‪ ,‬לפעמים נרצה לקבל מידע על‬ ‫ההגדרה עצמה‪ ,‬לדוגמה‪ :‬אם נרצה לתת למשתמש את היכולת להחליט איזה מתודה‬ ‫להפעיל מכל אובייקט שרק יהיה‪ ,‬אין לנו דרך לכתוב את הקוד בצורה רגילה‪ ,‬מכיוון‬ ‫שאנחנו לא יודעים איזה אובייקט ואיזה מתודה נרצה להפעיל‪ ,‬אמנם זוהי דוגמה‬ ‫טיפשית במקצת‪ ,‬אבל דוגמה טובה מתי נרצה לעשות משהו שמתאפשר רק בעזרת‬ ‫‪.Reflection‬‬ ‫הקוד האמתי שנעשה יהיה לרוב במקומות בהם נרצה לכתוב תשתיות‪ ,‬למשל בהינתן‬ ‫שיש לנו מחלקה אבסטרקטית בשם ‪ ,Logger‬שיש מספר מחלקות שיורשים ממנה‬ ‫)‪ (Console Logger, Mail Logger, File Logger‬ונרצה לטעון דינמית לפי הגדרה בקונפיג‬ ‫את הלוגר המתאים‪ ,‬בקוד סטנדרטי‪ ,‬נקראת את התוכן מקובץ הקונפיג‪ ,‬ונבצע פעולת‬ ‫‪ if‬כדי לבדוק איזה מחלקה צריכה להיטען‪ ,‬יש עם זה מספר בעיות‪ ,‬ראשית זה די ‪Hard‬‬ ‫‪ code‬ואם יהיה לנו מחלקה חדשה )‪ (Web Service Logger‬נצטרך לשנות את הקוד‬ ‫שלנו‪ ,‬שנית תמיד נצטרך להכיר מראש בקוד שלנו את כל סוגי ה – ‪.Loggers‬‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫כדי לפתור זאת ניתן להשתמש ב – ‪ Reflection‬וליצור דינמית לפי שם המחלקה את‬ ‫האובייקט‪.‬‬ ‫‪ Reflection‬מאפשר לנו‪ ,‬לבדוק אילו מתודות‪ ,‬מאפיינים‪ ,‬משתנים וכדו' קיימים‬ ‫במחלקה‪ ,‬ומאפשר לנו להפעיל אותם או לשנות אותם לפי השם שלהם )גם הם‬ ‫מוגדרים כ – ‪ ,(Private‬חשוב לזכור ‪ Reflection‬הוא לא משהו גרוע‪ ,‬זה מאפשר לנו‬ ‫לכתוב תשתיות שלפעמים אין דרך אחרת לממש אותם‪ ,‬אבל מצד שני זה יכול לאפשר‬ ‫לנו לעשות שטויות )כמו לשנות ‪ private‬של מחלקות(‪ ,‬יש להשתמש במנגנון לפי הצורך‬ ‫ובצורה חכמה‪.‬‬

‫‪Attribute‬‬ ‫כבר ראינו דוגמה לשימוש ב – ‪ Attribute‬למי שזוכר‪ ,‬כשלמדנו על ‪ enums‬הכרנו את‬ ‫‪ Flags‬שמגדיר כיצד להדפיס ‪ enums‬שיכול להיות יותר מערך אחד‪.‬‬ ‫‪ attribute‬הינו מנגנון שמאפשר לנו לשים "מדבקות" או מידע נוסף על אובייקט‪ ,‬אנחנו‬ ‫מגדירים אותם כמידע על האובייקט בשונה ממאפיינים למשל שזה מידע של האובייקט‬ ‫)שימו לב להבדל בין "על" לבין של"(‪ ,‬מידע על האובייקט הוא מידע שלא מעניין את‬ ‫האובייקט אלא מתייחס לאלמנטים אחרים שירצו להשתמש באובייקט וצריכים לדעת‬ ‫כיצד‪ ,‬כמו למשל ‪ Serializable‬שמגדיר למחלקה שמתעסקת עם סירליזציה ל – ‪xml‬‬ ‫האם האובייקט ניתן להמרה ל – ‪ ,xml‬או ‪ DebuggerDisplay‬שמאפשר לנו להגדיר ל‬ ‫– ‪ debugger‬כיצד להציג את האובייקט כשיש ‪ ,Break Point‬ואנחנו מסתכלים על‬ ‫האובייקט‪.‬‬ ‫יצירת ‪ Attribute‬היא פשוטה‪ ,‬בסך הכול צריך לכתוב מחלקה היורשת מ – ‪,Attribute‬‬ ‫וכל מה שצריך לעשות זה לשים את המחלקה החדש שלנו על אלמנט כלשהו )מחלקה‪,‬‬ ‫מאפיין וכדו'(‪ ,‬הנקודה שזה לא ישפיע על שום דבר‪ ,‬מכיוון שאם אף אחד )שום קוד‬ ‫חיצוני( לא יבדוק האם השתמשו בו‪ ,‬לא יקרה שום דבר‪.‬‬ ‫כדי לבדוק האם יש ‪ Attribute‬יש צורך להשתמש ב – ‪ ,Reflection‬כל אלמנט כמו‬ ‫)‪ FieldInfo, PropertyInfo‬וכדו( יש להם מתודה בשם ‪GetCustomAttribute‬‬ ‫שמאפשר לקבל גישה לאותם מחלקות שהשתמשנו בהם‪.‬‬ ‫ממשקים‬

‫סיכום קורס ‪ C#‬בסיסי‪ :‬מרצה‪ :‬הרב דוטנט‪.‬‬ ‫ראשית כדי לא להתבלבל בין ירושה לבין ממשק )‪ ,(Interface‬נגדיר כך‪ :‬אנחנו יורשים‬ ‫)מרחיבים( מחלקות‪ ,‬ואנחנו מממשים ממשקים )‪ .(interfaces‬ההבדל הינו מהותי‪,‬‬ ‫ירושה מקבלת את מה שיש לאבא ומאפשר להוסיף או לשנות‪ ,‬מימוש‪ ,‬הינו בסך הכול‬ ‫הצהרה על יכולות שיש לנו‪ ,‬יכול להיות שיהיה לנו את היכולות הללו בלי קשר‪ ,‬אבל‬ ‫אם נצהיר עליהם נוכל לקבל תוספות‪.‬‬ ‫לדוגמא‪ ,‬יש פונקציה בשם ‪ Array.Sort‬שיודעת לקבל כל מערך שהוא ולמיין אותו‪,‬‬ ‫בתנאי שהצהיר על עצמו שהוא מממש את ‪ ,IComparable‬שבסך הכול מגדיר שיש‬ ‫למממש מתודה שמקבלת שני אובייקטים ומחזירה האם צריך להחליף ביניהם או לא‪,‬‬ ‫יכולנו להחזיק את המתודה ללא קשר למימוש‪ ,‬אבל מתודת ‪ Array.Sort‬לא תדע‬ ‫להשתמש בפונקציה מכיוון שלא הצהרנו על כך‪.‬‬ ‫הגדרת ממשק מאפשרת שימוש בדומה לירושות‪ ,‬שמצביע של אבא יכול להסתכל על‬ ‫בן‪ ,‬מה שמאפשר לנו לכתוב מתודה שמקבלת ממשק‪ ,‬ונוכל לשלוח אליה כל מה‬ ‫שמממש את הממשק‪ ,‬בלי שיהיה אכפת לנו בזמן כתיבת הקוד מיהו אותו אובייקט‪.‬‬ ‫דוגמאות לממשקים‪:‬‬ ‫•‬

‫‪ – IFormatable‬המאפשר לנו להשתמש במחלקות שלנו עם ‪string.Format‬‬

‫•‬

‫‪ – IComparable‬המאפשר השוואה בין שני אוביקטיים‪.‬‬

‫•‬

‫‪ - IEnumerable‬המאפשר להשתמש עם ‪ foreach‬על האובייקטים שלנו‪.‬‬

סיכום CSHarp בסיסי2.pdf

Page 2 of 16. סיכום קורס #C בסיסי: מרצה: הרב דוטנט. תוכן העניינים: • הקדמה ל – net.Microsoft. • היכרות עם ה – System Type. • עבודה עם מחלקות. • מנגנון ניקוי הזיכרון.

160KB Sizes 23 Downloads 85 Views

Recommend Documents

No documents