C sharp

מתוך ויקיפדיה, האנציקלופדיה החופשית
קפיצה אל: ניווט, חיפוש
C#‎
C Sharp wordmark.svg
פרדיגמות מרובת פרדיגמות: תכנות מונחה-עצמים, תכנות מבני, תכנות אימפרטיבי, תכנות פונקציונלי, תכנות מונחה-אירועים, תכנות גנרי
מתכנן חברת מיקרוסופט
מפתח חברת מיקרוסופט
גרסה אחרונה 5.0 (אוגוסט 2012)
טיפוסיות חזקה, בטוחה, (סטטית עם תמיכה בטיפוסיות דינמית)
מימושים Visual C#, Mono
ניבים Cω, Spec#, Polyphonic C#
הושפעה על ידי ++C,‏ Smalltalk,‏ Java,‏ Visual Basic, אייפל, Modula-3
השפיעה על D‏, F#, Java, Nemerle, Vala
סיומת cs.

C#‎ (קרי C Sharp או "סִי שַׁארפּ") היא שפת תכנות עילית מרובת-פרדיגמות, מונחית עצמים בעיקרה, המשלבת רעיונות כמו טיפוסיות חזקה, אימפרטיביות, הצהרתיות, פונקציונליות, פרוצדורליות וגנריות. השפה פותחה על ידי מיקרוסופט בשנת 2000 כחלק מפרויקט דוט נט ותוקננה בשנים 2005 ו-2006 על ידי ארגון התקינה Ecma כתקן ECMA-334 וארגון התקינה הבינלאומי (איזו) כתקן ISO/IEC 23270:2006.

היסטוריה[עריכת קוד מקור | עריכה]

לידתה של שפת C#‎ החלה עם פיתוח המכונה הווירטואלית של דוט נט CLI לניהול זמן הריצה של יישומים. בשל מגבלות בשפות התכנות הנפוצות התקשו אדריכלי דוט נט ליצור סביבת ריצה אחידה לכל השפות. לאחר הפסדה של מיקרוסופט במשפט לחברת סאן, בעקבות סכסוך שפרץ ביניהן בנושא תאימויות חויבה על פי פסק הדין להפסיק לשווק את המימוש שלה ל-Java שנקרא J++‎ וכן המכונה הווירטואלית שפיתחה עבורה MS-JVM. עקב כך הוחלט במיקרוסופט על פיתוח שפה חדשה במטרה להוות תחליף ראוי לשפות התכנות מהדור הקודם כמו ויז'ואל בייסיק או C++‎ וכן להוות יריב שקול ל-Java. צוות פיתוח בראשות מהנדס התוכנה הדני אנדרס היילסברג (מי שהמציא את טורבו פסקל והיה מאדריכלי דלפי) הופקד על פיתוחה בתחילת 1999 והגרסה הראשונה הושקה עם ההכרזה על דוט נט ביולי 2000‏[1].

ב-2 באפריל 2014 שחררה מיקרוסופט גרסת בטא למהדר חדש בשם DotNET Native‏[2], המאפשר להדר קוד בשפת #C לקוד מכונה באופן ישיר (Native Code), ללא שימוש ב-JIT (מהדר דינמי - Just-In-Time Compiler). לפי מיקרוסופט, מהדר זה מתבסס על המהדר הקיים של Visual Studio לשפת ++C, והוא מהדר סטטי. מהדר זה כרגע זמין ליישומי Windows Store בלבד (אפליקציות לטאבלטים של מיקרוסופט), אך לפי מיקרוסופט, בעתיד הקרוב המהדר יתמוך גם ביישומי Windows למחשבים נייחים (Desktop Applications). היתרון הבולט במהדר זה הוא הביצועים - ניתן לכתוב קוד בשפת #C עם ביצועים זהים לקוד בשפת ++C, לפי מיקרוסופט. בנוסף לכך, מהדר זה מאפשר ל-#C לרוץ על מספר פלטפורמות שונות, ומקשה על פענוח לשפת המקור של הקובץ המהודר (Assembly Executable). יתרון נוסף הוא שלמרות שהקוד הוא קוד מכונה, ניהול הזיכרון האוטומטי ושאר היתרונות של .NET עדיין מתאפשרים באופן מלא לחלוטין, בניגוד לשפות אחרות שמתהדרות לקוד מכונה (כדוגמת ++C). מהדר זה לא יחליף את מהדר ה-JIT, אך הוא יספק אלטרנטיבה למהדר ה-JIT (ניתן לקבוע את סוג ההידור, JIT או Native, לפני ההידור).

הדמיון ל-Java[עריכת קוד מקור | עריכה]

קיימים קווי דמיון רבים בין שפת C#‎ ל-Java, כגון; מכונה וירטואלית, קוד ביניים, הידור דינמי (JIT), ירושה יחידה עם מנשקים, זיכרון מנוהל, שיקוף, Boxing ועוד. ובכל זאת, קיימים הבדלים גדולים בין השתיים. בעיקר בהגדרת אופן מימוש מנגנוני השפה, השוני במימוש תכנות גנרי. מלבד זאת בסי שארפ נוספו תכונות שבחלקן אומצו מ-VB כגון תכנות ויז'ואלי, תכנות מונחה אירועים, אתחול אוטומטי של משתנים, משתנה רב תכליתי, מאפיינים, לולאת טווח ועוד. במיוחד גדל הפער בין השפות בגרסאות החדשות של סי שארפ בהן נוספו הרחבות כמו תכנות פונקציונלי, מתודות-הרחבה (Extension methods) והרחבת LINQ.

פיתוח השפה היה ראשוני, ונטול עכבות היסטוריים כמו תאימות לאחור, שהיקשה על האבולוציה של Java. ברור גם כי העובדה ש-C#‎ פותחה אחרי Java איפשרה למיקרוסופט ללמוד מהחסרונות והיתרונות שלה וליישם את הלקחים בשפה החדשה‏[3].

אנשי סאן טענו כי מיקרוסופט גנבה לא מעט רעיונות משפת Java. לטענתם, C#‎ היא בעצם חיקוי לא מוצלח של Java ממנו הוסרו אמינות, יצרנות ואבטחה‏[4]. במיקרוסופט מתעקשים לעומת זאת כי השפה דומה יותר ל-C++‎ מאשר ל-Java.

ההבדל המהותי ביותר בין #C ל- Java הוא מתודולוגי, #C היא שפה מונחית עצמים טהורה (Pure Object Oriented), ב- #C כל דבר הוא אובייקט, אפילו הטיפוסים הפשוטים (int, double, float, char ועוד) הם אובייקטים.

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

יעדי השפה[עריכת קוד מקור | עריכה]

תקן EMCA מפרט את יעדי הפיתוח של השפה:

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

התחביר של C#‎ מבוסס בעיקרו על שפת התכנות C++‎ אך הקונספט התכנותי הושפע רבות מ-VB‏[5] ו-Java. מהם אומצו מספר מוטיבים כמו תכנות מונחה-אירועים, תכנות הצהרתי, תכנות פונקציונלי ותכנות גנרי.

איפיוני השפה[עריכת קוד מקור | עריכה]

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

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

קוד של C#‎ מחשב שורשים של פונקציה בעזרת שיטת ניוטון-רפסון ושיטת החצייה.

בשפה שולבו מספר מאפיינים בולטים שהם חלק אינטגרלי מהקונספט הטכנולוגי של דוט נט:

  • תמיכה בסריאליזציה‏[6] שבה נשמר רצף בין הזיכרון לדיסק, כך ניתן לשמור אובייקט לקובץ ולשחזר אותו.
  • מקביליות (Concurrency) שבה ניתן לבצע פעילות אסינכרונית של עצמים שונים.
  • התבוננות פנימה (Reflection), כלומר חקירת מאפיינים שונים הקשורים לשפה עצמה בזמן ריצה, למשל לבדוק מהו הטיפוס המדויק של אובייקט נתון.
  • תכונות (Attributes) שמאפשרים להגדיר מגוון רחב של עזרים בקוד התכנותי, כמו פירוט תכונות של מחלקות, שיטות ומשתנים, הגדרת תהליכים תכנותיים ועד הסבר מובנה. מה שמייתר צורך בקובצי הגדרה חיצוניים כמו DEF ו-IDL.

מערכת הטיפוסים[עריכת קוד מקור | עריכה]

הטיפוסיות בשפה היא סטטית (עם תמיכה בפולימורפיזם של זמן ריצה), ותמיכה בטיפוס dynamic, המאפשר עבודה ב"טיפוסיות-ברווז" דינמית‏[7].

מערכת הטיפוסים היא חזקה ובטוחה, למעט באזורים בקוד המסומנים באמצעות המילה השמורה unsafe. באזורים אלה מתאפשרת גישה ישירה לזיכרון בעזרת מצביעים (בדומה לשפת C++‎)‏[8].

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

משתני ערך לעומת משתני ייחוס[עריכת קוד מקור | עריכה]

בשפת #C ישנה חלוקה של הטיפוסים לשני סוגים:‏[11]

  • טיפוסי התייחסות (Reference Types): בעלי סמנטיקת התייחסות, בדומה למצב בשפת ג'אווה. מחלקות, מערכים, נציגים (delegates) ממשקים, טיפוסים דינמיים ומחרוזות הם טיפוסי התייחסות.‏[12]
  • טיפוסי ערך (Value Types): בעלי סמנטיקת ערך, בדומה למצב בשפת ++C. טיפוסי ערך יורשים מהמחלקה ValueType (שבתורה יורשת מ-object). טיפוסי ערך הם מבנים (struct),‏ enum, והטיפוסים הפרימיטיביים (כגון int‏, double ו-‏char).‏[13]. עבור טיפוסי ערך קיים מנגנון אריזה (Boxing) ההופך אותם לטיפוסי התייחסות כאשר הדבר נדרש.‏[14]

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

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

בעוד שמחלקה היא מבנה התכנות העיקרי, מבנים מיועדים לעטוף קבוצות קטנות של משתנים קשורים, בעיקר כאלה שאינם אמורים להשתנות לאחר היצירה של האובייקט.‏[15]

אין לבלבל בין טיפוסי ערך והתייחסות ובין העברת פרמטרים על פי ערך ועל פי התייחסות (ר' בהמשך).

העברת פרמטרים[עריכת קוד מקור | עריכה]

ברירת המחדל של העברת פרמטרים למתודה היא על ידי העתקה: אובייקט מטיפוס ערך מועתק בעצמו, ועבור אובייקט מטיפוס התייחסות מועתקת ההתייחסות אליו.‏[16] ניתן לשלוט על כך באמצעות הגדרת הפרמטרים כ-ref או כ-out (הן במתודה והן בקריאה אליה). פרמטר המוגדר כאחד מהם דורש משתנה ממש (או ביטוי שניתן לבצע אליו השמה - lvalue. ולא אובייקט); משתנה זה מועבר בעצמו - ללא העתקה - ופעולות השמה בתוך המתודה מתבצעות ישירות עליו.

  • העברת משתנה כ-ref מחייבת אתחולו לפני הפעלת המתודה, והמתודה רשאית לקרוא ממנו או לכתוב אליו.‏[17]
  • העברת משתנה כ-out מאפשרת לדמות החזרה של מספר ערכים מהמתודה. המתודה איננה רשאית לקרוא מהמשתנה אלא רק לכתוב אליו.‏[18]

ניתן להגדיר מתודה המקבלת מספר משתנה של פרמטרים בעזרת המילה השמורה params.‏[19]

סוגי משתנים[עריכת קוד מקור | עריכה]

ניתן להגדיר משתנה קבוע (const) המאותחל בזמן הקישור.‏[20] ניתן להגדיר משתנה קריאה-בלבד (readonly) היכול להיות מאותחל בזמן ריצה באמצעות הבנאי.‏[21]

תכנות מונחה-עצמים[עריכת קוד מקור | עריכה]

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

ירושה[עריכת קוד מקור | עריכה]

בשפת #C כל מחלקה יורשת ממחלקה בודדת, וישנה אפשרות לָרֶשֶת ("לממש" במינוחי ג'אווה) מספר ממשקים (Interfaces): מחלקות הכוללות מתודות אבסטרקטיות בלבד, ומשמשות כחוזים המכריחים את המחלקות היורשות לממש את המתודות המוגדרות בממשק. ב#C קיימת ירושה ציבורית בלבד; לא ניתן להחיל הגבלות גישה על מחלקות האם.

סוגים שונים של מחלקות:‏[22]

  • מחלקה אבסטרקטית (abstract) היא מחלקה המשמשת כמחלקת אם בלבד; לא ניתן ליצור עצמים של המחלקה שלא דרך מחלקה יורשת, והיא מגדירה טיפוס משותף, ממשק משותף (אם מוגדרות בה מתודות) ומימוש ברירת מחדל (אם מוגדרות בה מתודות לא-אבסטרקטיות או משתנים) עבור מחלקות יורשות.
  • מחלקה חתומה (sealed): מחלקה שלא ניתן לרשת ממנה (מקביל ל-final בג'אווה).‏[23]
  • מחלקה סטטית: מחלקה שלא ניתן ליצור מופעים שלה כלל. תפקידה הוא לאגד מתודות סטטיות (שהן למעשה פונקציות) קשורות בינן לבין עצמן.‏[24] מחלקות סטטיות מאפשרות לכתוב מתודות-הרחבה (ראה בהמשך) עבור מחלקות אחרות.

מתודות (שיטות)[עריכת קוד מקור | עריכה]

בשפת C#‎ לא ניתן לכתוב פונקציות גלובליות או להגדיר משתנים גלובליים כמו בשפת C++‎ ו-VB. כל המתודות חייבות להיות מאוגדות במחלקות, ובפרט הפונקציה Main, שהיא נקודת הכניסה (ההתחלה) של התוכנית.

התחליף לפונקציה גלובלית הוא מתודה סטטית (static), המופעלת דרך שם המחלקה שלה בלי הצורך ליצור מופע. כתחליף למשתנה גלובלי ניתן להגדיר משתנה סטטי במחלקה. מתודה סטטית איננה יכולה לגשת למשתנים רגילים ללא התייחסות לאובייקט ספציפי, אך יכולה לגשת למשתנים סטטיים, אותם ניתן לאתחל באמצעות בנאי סטטי. ניתן להגדיר מחלקה כסטטית, ובמחלקה כזאת ניתן להגדיר רק מתודות סטטיות. לדוגמה, המחלקה Console היא סטטית, וכך גם המתודות הכלולות בה כגון WriteLine. סוג מיוחד של מתודות סטטיות הן מתודות-הרחבה (extension methods).‏[25]

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

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

ניתן למנוע דריסה של מתודה וירטואלית במחלקה נגזרת על ידי הגדרתה כמתודה חתומה (sealed) במחלקת הבסיס.‏[27]

C#‎ מאפשרת העמסת מתודות, שהיא האפשרות לכתוב מתודות בעלות שם זהה, וחתימה שונה (רשימת הפרמטרים צריכה להיות שונה בטיפוסי הפרמטרים או במספרם). כמו כן ניתן ליצור מתודה עם מספר לא ידוע של פרמטרים (באמצעות מילת המפתח params והגדרת מערך ברשימת הפרמטרים. (לדוגמה: (public void functionName(params String[] str1). גם ניתן ליצור ערכי בררת מחדל בפרמטרים של מתודה באופן דומה לכתיבה ב-C#‎.

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

הסתרה וחלוקה למודולים[עריכת קוד מקור | עריכה]

יחידת ההסתרה ב-#C היא המחלקה (ולא האובייקט). ישנן חמש רמות הרשאה לגישה לשדות:

  1. private: מותרת גישה אך ורק מתוך המחלקה הנוכחית.
  2. protected: מותרת גישה מתוך המחלקה הנוכחית ומחלקות יורשות.
  3. public: מותרת גישה מכל מקום.
  4. internal: מותרת גישה מכל מקום בתוך האסמבלי של המחלקה, שהוא הקובץ בשפת-הביניים אותו מריצה סביבת הNET. לאחר שהתוכנית עוברת הידור.
  5. protected internal: מותרת גישה בתוך היישום ומתוך מחלקות יורשות, גם אם הן באסמבלי אחר.

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

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

מאפיינים (Properties)[עריכת קוד מקור | עריכה]

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

בדומה למוסכמה השכיחה ב-Java לכנות מתודות שתפקידן השמה ואחזור של ערכים get ו-set בהתאמה ולאחריהן שם התכונה. אומץ רעיון ה"מאפיינים" מ-Visual Basic כדי להקל על השמה ואחזור שדות פרטיים. בתפיסה זו מאפיין מורכב משדה ושיטות הפועלות עליו: האחת קוראת (getter) והאחרת כותבת (setter). כל שנדרש להגדיר הוא הגבלת ההרשאה (Access Modifier) על פי רעיון הכימוס, לקבוע מה יהיה הערך המוחזר ולקבוע את שם המאפיין. מימוש המאפיין מתנהג כפונקציה חברה לכל דבר, כך ניתן לשלוט בערכים המושמים והמאוחזרים בזמן הריצה. בכל פעם שתתבצע גישה למאפיין היא תופנה ל-get וכל פעם שיוצב ערך במאפיין תתבצע הפעלה של set. כמו כן, החל מגרסה 2.0, ניתן לקבוע רמות הרשאה שונות ל-get ול-set (כך, לדוגמה, ניתן לקבוע הרשאת גישה ציבורית לאחזור get ולהגביל את השמת הערך set לפרטית כך רק מחלקות יורשות יוכלו לשנות את השדה - במקרה כזה המחלקה תקרא Immutable דהיינו לא ניתנת לשינוי. או להסיר את ההשמה לחלוטין, במקרה כזה יקרא המאפיין "קריאה בלבד"). מעבר לעובדה שהמאפיינים מהווים רמת הפשטה למשתמש, הם הופכים את השדה לשדה חכם (smart field) שמאפשר לבצע פעולות נוספות בעת השמה או אחזור, כמו בדיקות תקינות ו/או חריגה או לבצע אתחול עצל, שהוא אתחול בזמן שימוש בשדה ולא בזמן יצירת המחלקה, וכן פעולות לוגיות אחרות.

class Student
{
    int _age;
    public int Age
    {
        get { return _age; }
        set { _age = value; }
    }
}

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

  Student student = new Student();
  student.Age = 26;
  Console.Write(student.Age);

והמימוש במחלקה אחרת יראה באופן הבא: ההשמה תשתמש במתודה set, והתצוגה תשתמש במתודה get של המאפיין. בגרסה 3 ומעלה נוספו מספר יכולות חדשות ביניהן היכולת ליצור "מאפיינים אוטומטיים"‏[28] - אם יש צורך בגישה לחבר פרטי ללא לוגיקה כלשהי, ניתן ליישמו בדרך ישירה וחסכונית כמו בדוגמה הבאה. במקרה זה המהדר מייצר מאחורי הקלעים שדה מגבה שהגישה אליו אפשרית רק דרך המאפיינים, כאילו נכתב הקוד לעיל:

public int Age { get; set; }

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

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

העמסת אופרטורים[עריכת קוד מקור | עריכה]

C#‎ מאפשרת העמסת אופרטורים - שהיא טכניקה המאפשרת לתת משמעות חדשה לרוב האופרטורים הקיימים בשפה‏[29], על מנת להקל על הקריאות של התוכנית. את האופרטור "[ ]" מעמיסים בצורה מעט שונה מהאחרים, בעזרת מנגנון הנקרא סדרן - Indexer הדומה בכתיבתו למאפיין:

private int arr = new int[100];     
public int this[int i] 
{
   get { return arr[i]; }
   set { arr[i] = value; }
}

תכנות פונקציונלי[עריכת קוד מקור | עריכה]

נציג (Delegate) הוא טיפוס המובנה בשפה, המגדיר חתימה של פונקציה, וכל מופע שלו מחזיק רשימה של עצמים ברי-קריאה המתאימים לחתימה זאת‏[30]. קריאה לנציג יוזמת קריאה של כל האובייקטים המוחזקים בו. ניתן לרשום (Subscribe) פונקציות לנציג בעזרת אופרטור =+, ולקרוא להן בעזרת תחביר רגיל של קריאה לפונקציה.

דוגמה לתוכנית "שלום עולם" בעזרת נציגים:

class HelloWorld
{
    public delegate void Del();
    static void Main(string[] args) {
        Del x = delegate { Console.Write("hello "); };
        x += delegate { Console.WriteLine("world"); };
        x();
    }
}

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

ישנם מספר מימושים גנריים ל-Delegates בספריה הסטנדרטית של C#‎:

  • Action: פונקציה המקבלת פרמטרים אך לא מחזירה דבר‏[32].
  • Func: פונקציה המקבלת פרמטרים כלשהם ומחזירה טיפוס כלשהו.
  • Predicate: פונקציה המקבלת פרמטר אחד ומחזירה טיפוס בוליאני.
  • Comparison: פונקציה המקבלת שני פרמטרים בני אותו טיפוס, ומחזירה ערך מספרי. נועדת להשוואה בין אובייקטים‏[33].

דוגמאות:

Action<> printHello = delegate { Console.Write("hello world!"); };
 
Predicate<int> range255 = delegate(int x) {return x>=0 && x<=255;};
 
Func<string, string> convert = delegate(string s) {return s.ToUpper();};

פונקציות אנונימיות וביטויי למדא[עריכת קוד מקור | עריכה]

החל מגרסה 2 שפת C# תומכת במתודות אנונימיות - פונקציה הנכתבת בשורת פקודה, ומוגדרת בזמן ההרצה עצמה‏[34]. החל מגרסה 3 נוספה תמיכה בביטויי למדא לשפה. זוהי דרך נוספת תמציתית וגמישה לכתוב מתודה אנונימית‏[35]. שימוש בפונקציות אנונימיות נעשה כאשר רוצים להשתמש בפונקציה באופן מקומי מבלי להגדירה במחלקה, או כאשר פרמטר של פונקציה מוגדר כפונקציה, ואז ניתן לכתוב את הפונקציה האנונימית בקריאה לפונקציה, מבלי להגדירה כלל כפונקציה במחלקה.

בדוגמה מימוש של הפונקציה שלעיל באמצעות ביטוי למדא. (האופרטור <= מפעיל הלמדא, מפריד בין הפרמטרים של הקלט בצידו השמאלי לגוף הלמדא מצידו הימני)

 
  Func<string, string> Convert = x => x.ToUpper();

בביטויי למדא נעשה שימוש רב ב-LINQ. בדוגמה איתור גודל המילה הקטנה ביותר במערך מחרוזתי מתקבל במשתנה shortestWordLength.

 
  string[] wordsArray = {"Orange", "Apple", "Pineapple", "Pear", "Peach"};
  int shortestWordLength = wordsArray.Min(w => w.Length);

תכנות מונחה אירועים[עריכת קוד מקור | עריכה]

event (אירוע) הוא טיפוס Delegate המובנה בשפה בעל הרשאות גישה מיוחדות: ניתן לרשום לאירוע פונקציות גם מחוץ למחלקה בה הוא מוגדר, אך הקריאה אליהן מתבצעת רק מתוך המחלקה.‏[36]

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

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

במחלקה המתארחת : CalculationEvent

class CalculationEvent
{
    public delegate int MultiplicationEventHandler(int x, int y);
    public event MultiplicationEventHandler MultiplicationNum;
 
    public int RaiseCalc(int x, int y)
    {
        return MultiplicationNum(x, y);
    }
}

במחלקה המארחת: HostCalculationEvent

class HostCalculationEvent
{
    CalculationEvent testCalculation = new CalculationEvent();
 
    public HostCalculationEvent()
    {
        testCalculation.MultiplicationNum += new CalculationEvent.MultiplicationEventHandler(testCalculation_MultiplicationNum);
    }
 
    int testCalculation_MultiplicationNum(int x, int y)
    {
        return x * y;
    }
}

תכנות גנרי[עריכת קוד מקור | עריכה]

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

public class GenericList<T> {
    void Add(T input) { }
}

ניתן להעביר כפרמטר כל טיפוס - פרימיטיבי, טיפוס-ערך או טיפוס-התייחסות:

class TestGenericList {
    private class ExampleClass { }
    static void Main() {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();
 
        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();
 
        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
    }
}

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

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

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

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

תכנות ויזואלי[עריכת קוד מקור | עריכה]

תמונת מסך של כלי פיתוח לתכנות חזותי ב-WinForm ב-C#, טופס, פקדים, רכיבים, חלונית Toolbox, חלונית מאפיינים, חלונית Document outline.

השפה תומכת בתכנות ויזואלי בעיקר בממשקי משתמש, באמצעות ספריות סטנדרטיות של הדוט נט וכלי פיתוח של הסטודיו, כאשר אלמנטים בשפה כ-Events ו-Properties משתלבים בפיתוח הוויזואלי ומקלים עליו. ניתן ליצור טפסים (Form) ליישומים שולחניים באמצעות ה-Winform, דפים (Page) ליישומי אינטרנט באמצעות ה-WebApplication, חלונות (Window) ליישומי גרפיקה מתקדמים באמצעות מודל התכנות WPF, ומסכים (Screen) ביישומי משחקים.

הקונספט שנלקח מ-VB והורחב והועצם, הוא יצירת אובייקט ראשי המסמל טופס, דף או חלון בהתאם לסוג היישום, שיכול להכיל בתוכו פקדים שהם מודלים תכנותיים קטנים, המיוצגים באמצעות צלמיות, שיכולים לתקשר זה עם זה. על גבי הטופס ניתן ל"צייר" ולמקם פקדים באמצעות גרירתם מחלונית "ארגז כלים" (ToolBox), וניתן לראות את צורת הסידור שלהם באמצעות חלונית "מתאר מסמך" (Document outline). ישנה חלוקה פנימית בין פקדים (Controls) (שהם רכיבי תוכנה ויזואליים כמו פקד תיבת טקסט ופקד תמונה) לבין רכיבים (Components) (שהם רכיבי תוכנה לוגיים, למשל רכיב FileSystemWatcher שעוקב אחר שינויים בקבצים, או רכיב BackgroundWorker שמאפשר ליצור תהליכון נפרד עם אירועים). הקשר בין פעולות הממשק לקוד הוא דו כיווני: לכל פעולה ויזואלית מחולל קוד מתאים, למשל גרירה של פקד אל הטופס יוצרת הכרזה שלו בטופס ואתחול של מאפיינים מסוימים שנקבעו מראש, כמיקום הפקד בטופס, וגודלו, וכל שינוי בקוד מתורגם לייצוגים הוויזואליים בחזרה.

ב-WebApplication קיימים כ-90 פקדים ורכיבים שונים, וב-Winform קיימים כ-70 פקדים ורכיבים שונים. ניתן ליצור פקדי ורכיבי משתמש מותאמים אישית על בסיס פקדים ורכיבים קיימים, או פקדים ורכיבים חדשים. הפקדים מסווגים בדרך כלל לכמה קטגוריות עיקריות. ב-Winform למשל הם מסווגים לשיתופיים, מכולות (פקדים שמכילים פקדים אחרים), תפריטים, נתונים (לעבודה עם בסיסי נתונים), תיבות שיח ועוד. דרך העבודה האופיינית היא גישה אל הקוד מתוך הממשק והליכה מהכלל אל הפרט. תחילה יש ליצור את הטופס, לשרטט עליו את הפקדים, ולאחר מכן להגדיר את מאפייניהם השונים, והאירועים הדרושים ליישום באמצעות חלונית "מאפיינים" (Properties) שמציגה את המאפיינים והאירועים הקיימים בפקד, ואז ליצור אירוע באזור הקוד, ולכתוב בו את הקוד הנדרש כדי לממש את התוכנית.

זיכרון מנוהל[עריכת קוד מקור | עריכה]

במטרה למנוע בעיות הנובעות מניהול עצמאי של זיכרון כמו זליגת זיכרון ודריסת זיכרון, שפת C#‎, כחלק מטכנולוגיית NET., מוגדרת כ"קוד מנוהל" (Managed Code). המתכנת פטור מן האחריות לשחרור מפורש של זיכרון המוקצה לאובייקט. סביבת ההרצה (CLR - Common Language Runtime) היא המופקדת על שחרור הזיכרון שתופסים עצמים שאינם בשימוש יותר.

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

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

טיפול בחריגות[עריכת קוד מקור | עריכה]

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

תאימות לאחור[עריכת קוד מקור | עריכה]

כדי להקל על מפתחים להגר משפות אחרות[דרוש מקור], ובפרט משפת C++‎, אל שפת C#‎, נכללו בשפה "כינויים" למספר מחלקות ומבנים, הזהים לשמות המקבילים בשפת C++‎:

מחלקה כינוי תיאור גודל בבתים איתחול אוטומטי
System.Byte byte בית 1 0
System.Int16 short שלם קצר 2 0
System.Int32 int שלם 4 0
System.Int64 long שלם ארוך 8 0
System.Decimal decimal שלם דצימלי 12 0
System.Single float ממשי 4 0.0
System.Double double ממשי כפול 8 0.0
System.Boolean bool בוליאני 1 false
System.Char char תו יוניקוד בודד 2 /0
System.String string מחרוזת יוניקוד null

בנוסף, קיימים כינויים למחלקות שאינן קיימות כסוגי משתנים בשפת C++‎: הכינוי object למחלקה Object, וכן הקידומת unsigned משפת C++‎ תהיה בדרך כלל הקידומת u לכינוי המתאים (למעט byte, שהוא כבר unsigned, ולכן קיים הכינוי sbyte, שהוא byte עם סימן). למשל, unsigned int בשפת C++‎ מקביל ל-uint בשפת C#‎, שאינו אלא כינוי למבנה UInt32.

המבנה Char בשפת C#‎ מכיל נתון באורך 16 סיביות או יותר (קידוד UTF-16), כדי שיוכל להכיל תו יוניקוד, בניגוד ל-char של C++‎ שהוא על פי רוב באורך 8 סיביות (מתאים להכיל תו ASCII, או בית בודד).

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

גרסאות[עריכת קוד מקור | עריכה]

ל-C#‎ פורסמו מספר מהדורות, בדרך כלל בצמוד להפצת מהדורה חדשה של ויז'ואל סטודיו:

גרסה תאריך ‎.NET Framework ויז'ואל סטודיו
C# 1.0 ינואר 2002 ‎.NET Framework 1.0 Visual Studio .NET 2002
C# 1.2 אפריל 2003 ‎.NET Framework 1.1 Visual Studio .NET 2003
C# 2.0 נובמבר 2005 ‎.NET Framework 2.0 Visual Studio 2005
C# 3.0 נובמבר 2007 ‎.NET Framework 3.0
‎.NET Framework 3.5
Visual Studio 2008
Visual Studio 2010
C# 4.0 אפריל 2010 ‎.NET Framework 4 Visual Studio 2010
C# 5.0 אוגוסט 2012 ‎.NET Framework 4.5 Visual Studio 2012


תקציר הגרסאות
C# 2.0 C# 3.0 C# 4.0 C# 5.0
תכונות
שהוספו
  • ג'נריקס
  • טיפוסים חלקיים (Partial)
  • מתודות אנונימיות
  • איטרטורים
  • טיפוסים מאפשרים ערכי NULL
  • הגדרת מאפיין (setters ) כפרטי
  • Method group conversions (delegates)
  • טיפוס מרומז של משתנים מקומיים
  • מאתחל אובייקטים ואוספים
  • מימוש אוטומטי של מאפיינים
  • טיפוסים אנונימיים
  • מתודות הרחבה
  • ביטויי שאילתה
  • ביטויי למדא
  • עצי ביטויי
  • מתודות חלקיות
  • כריכה דינמית
  • ארגומנטים אופציונלים
  • Generic co- and contravariance
  • Embedded interop types ("NoPIA")
  • מתודות אסינכרוניות
  • Caller info attributes

דוגמה לתוכנית Hello world[עריכת קוד מקור | עריכה]

להלן דוגמה לתוכנית Hello world בשפה זו‏[37]:

using System;
 
public class ExampleClass
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Hello world!");
    }
}

ראו גם[עריכת קוד מקור | עריכה]

לקריאה נוספת[עריכת קוד מקור | עריכה]

  • כריסטוף וייל, היכרות עם C#‎, בהוצאת SAMS והוד עמי, ישראל 2001, 189 עמ'
  • טום ארצ'ר, C#‎ למתכנתי Java/C++/Visual C++, הוצאת מיקרוסופט והוד עמי, ישראל 2002, 411 עמ'
  • דאנקן מקניז, וקנט שארקי, C#‎ - סדנת לימוד, SAMS והוצאת הוד עמי, ישראל 2002, 800 עמ'
  • על כוס קפה, מדריך לשפת C#‎ ולמערכת .NET , עיטם מדעי המחשב, 2011

קישורים חיצוניים[עריכת קוד מקור | עריכה]

הערות שוליים[עריכת קוד מקור | עריכה]

  1. ^ Kovacs, James (September 7, 2007). C#/.NET History Lesson. אוחזר ב־June 18, 2009.
  2. ^ http://blogs.msdn.com/b/dotnet/archive/2014/04/02/announcing-net-native-preview.aspx
  3. ^ http://msdn.microsoft.com/en-us/library/ms836794.aspx
  4. ^ http://news.cnet.com/2008-1082-817522.html
  5. ^ "C# משלבת את היצרנות הגבוהה של Microsoft Visual Basic עם העוצמה הגלומה ב-C++" סקוט וויילטמוף (חבר בצוות הפיתוח של השפה) מצוטט ב-C# למתכנתי Java/C++/Visual C++, הוצאת מיקרוסופט והוד עמי, ישראל 2002, עמ' 14
  6. ^ http://msdn.microsoft.com/en-us/library/vstudio/ms233843.aspx
  7. ^ http://msdn.microsoft.com/en-us/library/vstudio/dd264741.aspx
  8. ^ http://msdn.microsoft.com/en-us/library/vstudio/chfa2zb8.aspx
  9. ^ http://msdn.microsoft.com/en-us/library/vstudio/bb383973.aspx
  10. ^ http://msdn.microsoft.com/en-us/library/vstudio/bb397687.aspx
  11. ^ http://msdn.microsoft.com/en-us/library/vstudio/ms173109.aspx
  12. ^ http://msdn.microsoft.com/en-us/library/vstudio/490f96s2.aspx
  13. ^ http://msdn.microsoft.com/en-us/library/vstudio/s1ax56ch.aspx
  14. ^ http://msdn.microsoft.com/en-us/library/vstudio/yz2be5wk(v=vs.120).aspx
  15. ^ "In general, classes are used to model more complex behavior, or data that is intended to be modified after a class object is created. Structs are best suited for small data structures that contain primarily data that is not intended to be modified after the struct is created.", [מקור http://msdn.microsoft.com/en-us/library/vstudio/ms173109.aspx]
  16. ^ http://msdn.microsoft.com/en-us/library/vstudio/8f1hz171.aspx
  17. ^ http://msdn.microsoft.com/en-us/library/vstudio/14akc2c7.aspx
  18. ^ http://msdn.microsoft.com/en-us/library/vstudio/ee332485.aspx
  19. ^ http://msdn.microsoft.com/en-us/library/vstudio/w5zay9db.aspx
  20. ^ http://msdn.microsoft.com/en-us/library/vstudio/e6w8fe1b.aspx
  21. ^ http://msdn.microsoft.com/en-us/library/vstudio/acdd6hb7.aspx
  22. ^ http://msdn.microsoft.com/en-us/library/vstudio/ms173150.aspx
  23. ^ http://msdn.microsoft.com/en-us/library/88c54tsw.aspx
  24. ^ http://msdn.microsoft.com/en-us/library/vstudio/79b3xss3.aspx
  25. ^ http://msdn.microsoft.com/en-us/library/vstudio/bb383977.aspx
  26. ^ http://msdn.microsoft.com/en-us/library/ebca9ah3.aspx
  27. ^ http://msdn.microsoft.com/en-us/library/88c54tsw.aspx
  28. ^ http://msdn.microsoft.com/en-us/library/bb384054.aspx
  29. ^ http://msdn.microsoft.com/en-us/library/vstudio/8edha89s(v=vs.120).aspx
  30. ^ http://msdn.microsoft.com/en-us/library/vstudio/ms173172.aspx
  31. ^ ThreadStart Delegate
  32. ^ http://msdn.microsoft.com/en-us/library/018hxwa8.aspx
  33. ^ http://msdn.microsoft.com/en-us/library/tfakywbh.aspx
  34. ^ http://msdn.microsoft.com/en-us/library/vstudio/0yw3tz5k.aspx
  35. ^ http://msdn.microsoft.com/en-us/library/vstudio/bb397687.aspx
  36. ^ http://msdn.microsoft.com/en-us/library/vstudio/8627sbea.aspx
  37. ^ על מנת שהתוכנית תמתין לתגובה מהמשתמש, יש להוסיף את הפקודה ()Console.Read לאחר השורה השלישית מהסוף.