תיעוד C#
מדריך עיון תמציתי ל-C#, מבוסס דוגמאות. קוראים את המושג, רואים את הקוד, ואז מתרגלים אותו במסלול של Coddy.
להתחיל מסלול C# מודרךצעדים ראשונים
- מה זה C#C# היא שפת תכנות כללית עם טיפוסים סטטיים מבית Microsoft שרצה על .NET. למה משתמשים בה, מה הקשר שלה ל-.NET, מה כל גרסה הוסיפה, ואיך בנויה תוכנית C#.
- Hello Worldהתוכנית הראשונה שלכם ב-C# שורה אחרי שורה: המתודה Main, Console.WriteLine ו-Write, קריאת קלט עם Console.ReadLine, הערות, יצירה והרצה של פרויקט עם ה-CLI של dotnet, ו-top-level statements.
- C# vs JavaC# ו-Java נראות דומות ופותרות בעיות דומות. ההבדלים האמיתיים הם במאפיינים, בטיפוסי ערך, ב-generics, ב-LINQ מול streams, ב-async, בחריגות ובאקוסיסטמות שסביבן. השוואה צד לצד עם קוד.
- C# vs C++C# היא שפה מנוהלת עם garbage collector; C++ מתקמפלת לקוד נייטיב ונותנת שליטה ידנית בזיכרון. איך ההבדל הזה בא לידי ביטוי בבטיחות, בביצועים, במנועי משחק, במודל הבנייה ובקושי ללמוד כל אחת מהן.
- Namespaces ו-usingאיך namespaces מארגנים טיפוסים ב-C# ואיך ההנחיה using מייבאת אותם: כינויים (aliases), using static, שמות מלאים, תיקון הפניות עמומות, global using ו-namespaces ברמת קובץ.
יסודות
- משתניםאיך מצהירים על משתנים ב-C# ומשימים להם ערכים, מתי var הוא רעיון טוב ומתי לא, איך טווח (scope) עובד, ההבדל בין const, readonly ו-static readonly, מוסכמות שמות, והשגיאה של משתנה שלא הושם לו ערך.
- טיפוסי נתוניםכל טיפוס מובנה של C# עם הגודל, הטווח והסיומת של הליטרל שלו, ההבדל בין טיפוסי ערך לטיפוסי הפניה, למה decimal הוא הטיפוס לכסף, גלישה של מספרים שלמים ו-checked, וערכי ברירת מחדל.
- String ל-intאיך ממירים מחרוזת ל-int ב-C# עם int.Parse, int.TryParse ו-Convert.ToInt32, איך המרות מרומזות ומפורשות עובדות, למה המרה של double חותכת את השבר, האופרטורים as ו-is, ולמה "1.5" יכול להתפרש כ-15.
- אופרטוריםהאופרטורים של C# וההתנהגות המדויקת שלהם: חילוק שלמים, אופרטור המודולו עם מספרים שליליים, הגדלה והשמה מורכבת, השוואה, לוגיקה מקוצרת, אופרטורי סיביות, אופרטורי null וטבלת קדימויות.
- מחרוזותאיך מחרוזות עובדות ב-C#: אי-שינוי, Length ואינדקסים, השוואה עם ==, Equals ו-string.Compare, תווי בריחה, מחרוזות verbatim עם @ וטקסט מרובה שורות, raw string literals, בדיקות null ומחרוזת ריקה, ומעבר על תווים בלולאה.
- Substringאיך String.Substring עובדת ב-C#: הארגומנטים של אינדקס ההתחלה והאורך, ArgumentOutOfRangeException ואיך להימנע ממנה, חילוץ טקסט סביב תוצאות של IndexOf, לקיחת n התווים הראשונים או האחרונים, ואופרטור הטווח מ-C# 8.
- String Interpolationאיך עובד string interpolation עם $"..." ב-C#: ביטויים בתוך סוגריים מסולסלים, מפרטי עיצוב כמו F2, N0, C, D5 ו-X, יישור לטבלאות, בריחה של סוגריים מסולסלים, string.Format ועיצוב מורכב, מחרוזות אינטרפולציה verbatim ו-raw, ועיצוב לפי תרבות מסוימת.
- מתודות מחרוזתמתודות ה-String שמשתמשים בהן כל יום ב-C#: Split עם כמה מפרידים ואפשרויות, Replace, Contains וחיפוש בלי תלות באותיות גדולות וקטנות, IndexOf ו-LastIndexOf, StartsWith ו-EndsWith, Trim, ToUpper ו-ToLower, PadLeft, Join, Concat והיפוך מחרוזת.
- StringBuilderלמה שרשור חוזר של מחרוזות בתוך לולאה איטי ב-C#, ואיך StringBuilder פותר את זה: Append, AppendLine, AppendFormat, Insert, Remove, Replace, קיבולת, ומתי שרשור רגיל או string.Join הם הבחירה הטובה יותר.
- מספרים אקראייםאיך יוצרים מספרים אקראיים ב-C# עם המחלקה Random: Next והגבול העליון שאינו כלול, מספרי double אקראיים בטווח, seed לתוצאות שחוזרות על עצמן, בחירת איבר אקראי, ערבוב רשימה, המלכודת של new Random בתוך לולאה, ומספרים מאובטחים קריפטוגרפית.
- DateTimeעבודה עם תאריכים ושעות ב-C#: יצירת ערכי DateTime, ההבדל בין Now, UtcNow ו-Today, הוספת ימים וחודשים, חיסור שנותן TimeSpan, TotalHours מול Hours, השוואת תאריכים, DayOfWeek, פענוח עם ParseExact ו-TryParse, DateTimeOffset ו-DateOnly.
- פורמט DateTimeאיך מעצבים DateTime כמחרוזת ב-C#: מחרוזות הפורמט הסטנדרטיות (d, D, o, s, u, t), תבניות מותאמות כמו yyyy-MM-dd HH:mm:ss, טבלה מלאה של מגדירי פורמט, איך התרבות משנה את הפלט, ISO 8601 עם round-trip באמצעות "o", והטעויות של mm מול MM ו-hh מול HH.
בקרת זרימה
- if elseאיך עובדים if, else if ו-else ב-C#: למה תנאים חייבים להיות bool, שילוב בדיקות עם &&, || ו-!, מתי סוגריים מסולסלים חשובים, guard clauses במקום קינון עמוק, והבאגים של = מול == ושל נקודה-פסיק מיותרת.
- פקודת switchאיך פקודת switch עובדת ב-C#: switch על ערכי int, string, char ו-enum, למה כל case צריך break (שגיאה CS0163), תוויות case מרובות לכמה ערכים, goto case, default, תלות באותיות גדולות וקטנות במחרוזות, ותוויות case עם תבניות ו-when.
- ביטוי switchביטוי ה-switch של C# 8 הופך ערך לתוצאה בעזרת זרועות עם תבניות, במקום case ו-break. תחביר, ה-discard _, תבניות מאפיינים, tuples ויחס, and/or/not, תנאי when, אזהרות כיסוי מלא ו-SwitchExpressionException, עם מקבילות להרצה ב-C# 7.
- אופרטור טרנריהאופרטור המותנה של C#, condition ? a : b, בוחר אחד משני ערכים. איך הוא מחושב, למה שני הענפים צריכים טיפוס משותף (CS0173) וההמרה שפותרת את זה, אופרטורים טרנריים מקוננים, סוגריים באינטרפולציית מחרוזות, ומתי ?? ו-?. מבטאים את זה טוב יותר.
- לולאת forאיך עובדת לולאת for ב-C#: האתחול, התנאי והקידום, ספירה לאחור וקפיצות, כמה משתני לולאה, מעבר על מערכים לפי אינדקס, לולאות מקוננות, for(;;), והבאגים של טעות באחד ושל מחיקה תוך כדי מעבר.
- לולאת foreachאיך עובדת foreach ב-C#: מעבר על מערכים, רשימות, מחרוזות ומילונים (KeyValuePair ופירוק), קבלת האינדקס, למה משתנה הלולאה הוא לקריאה בלבד, החריגה InvalidOperationException עם "Collection was modified" והפתרונות שלה, ומה טיפוס צריך כדי שיהיה אפשר להשתמש בו ב-foreach.
- לולאת whileאיך עובדות לולאות while ו-do while ב-C#: התנאי שנבדק לפני הגוף או אחריו, קריאת קלט עד ערך עצירה או עד סוף הקלט, while (true) עם break, וטעויות הלולאה האינסופית שרבים נופלים בהן.
- break ו-continueאיך break, continue ו-goto עובדים ב-C#: סיום לולאה מוקדם, דילוג על איטרציה, break בתוך switch, שלוש דרכים לצאת מלולאות מקוננות (דגל, goto, או מתודה עם return), והבאג של לולאה אינסופית עם continue בתוך while.
מתודות ו־delegates
- מתודותאיך מצהירים על מתודות ב-C# וקוראים להן: טיפוסי החזרה ו-void, פרמטרים, מתודות עם גוף ביטוי, החזרת כמה ערכים עם tuple, העמסה (overloading), מערכי params, מתודות סטטיות מול מתודות מופע, המתודה Main, רקורסיה ופונקציות מקומיות.
- פרמטרים אופציונלייםאיך עובדים פרמטרים אופציונליים וארגומנטים עם שם ב-C#: ערכי ברירת מחדל וכלל הקבוע בזמן קומפילציה, סדר הפרמטרים, דילוג על ארגומנטים לפי שם, פרמטרים אופציונליים מול העמסות, תכונות caller info, ומלכודת הגרסאות של ברירות מחדל שנצרבות בקוד הקורא.
- ref ו-outאיך עובדים פרמטרי ref, out ו-in ב-C#: העברת המשתנה עצמו במקום עותק, out לתוצאות נוספות ותבנית TryParse, out var, פרמטרי in לקריאה בלבד, כללי ההשמה הוודאית, ומה משתנה כשהארגומנט הוא טיפוס הפניה.
- ביטויי Lambdaאיך עובדים ביטויי lambda ב-C#: התחביר של => על כל צורותיו, שמירת lambdas ב-Func וב-Action, העברה שלהן למתודות ול-LINQ, statement lambdas, closures על משתנים שנלכדו, והבאג של לכידה בלולאת for שאין ב-foreach.
- Delegatesמהו delegate ב-C# ואיך משתמשים בו: הצהרה על טיפוס delegate, יצירה שלו ממתודה, מ-lambda או ממתודה אנונימית, הפעלה שלו, delegates כ-callbacks, multicast delegates עם += ו--=, בדיקת null עם ?.Invoke, ואיך delegates קשורים ל-Func, ל-Action ול-events.
- Func ו-Actionאיך עובדים Func ו-Action ב-C#: Func<T, TResult> לפונקציות שמחזירות ערך, Action<T> לפונקציות שלא, Predicate ו-Comparison, העברה והחזרה של פונקציות, מילון של פקודות, lambdas אסינכרוניות, ומתי עדיף להצהיר על delegate משלכם.
- Eventsאיך events עובדים ב-C#: הצהרה על event עם EventHandler ו-EventHandler<T>, מחלקות EventArgs מותאמות, הרשמה וביטול הרשמה עם += ו--=, הפעלה בטוחה של event עם ?.Invoke, למה event עדיף על שדה delegate ציבורי, ודליפת הזיכרון שנגרמת מביטול הרשמה שנשכח.
- Extension Methodsאיך עובדות מתודות הרחבה ב-C#: מתודה static במחלקה static עם this על הפרמטר הראשון, קריאה אליה כמו למתודת מופע, הרחבה של string, enum ו-IEnumerable<T>, איך LINQ בנוי מהן, למה מתודות מופע גוברות, מקבל null, והנחיית ה-using שהן צריכות.
אוספים ו־LINQ
- מערכיםמערך ב-C# הוא בלוק בגודל קבוע של איברים מטיפוס אחד. למדו איך מצהירים על מערכים ומאתחלים אותם, קוראים את Length, ממיינים ומחפשים עם המחלקה Array, מעתיקים בבטחה, ועובדים עם מערכים דו-ממדיים ומשוננים.
- ListList<T> הוא המערך הגדל של C#. למדו איך יוצרים רשימה, מוסיפים ומכניסים פריטים, מוחקים לפי ערך, אינדקס או תנאי, מחפשים עם Contains ו-Find, ממיינים לפי מאפיין, ונמנעים מהשגיאה של שינוי רשימה בתוך foreach.
- DictionaryDictionary<TKey, TValue> ממפה מפתחות לערכים עם חיפוש מהיר. למדו איך מוסיפים ומעדכנים רשומות, קוראים בבטחה עם TryGetValue, עוברים על רשומות KeyValuePair, סופרים הופעות, מתעלמים מאותיות גדולות וקטנות במפתחות, ושומרים על מפתחות ממוינים עם SortedDictionary.
- HashSetHashSet<T> מחזיק פריטים ייחודיים ועונה על Contains בזמן קבוע. למדו איך Add מדווח על כפילויות, איך מסירים כפילויות מרשימה, איך מאחדים, חותכים ומחסירים קבוצות, ואיך גורמים לקבוצה להשוות אובייקטים שלכם לפי ערך.
- Queue (תור)Queue<T> הוא אוסף מסוג "ראשון נכנס, ראשון יוצא": איברים יוצאים בסדר שבו הגיעו. למדו את Enqueue, Dequeue ו-Peek, את החריגה של תור ריק ואת TryDequeue, חיפוש לרוחב עם תור, ומתי לבחור ב-ConcurrentQueue או ב-PriorityQueue.
- Stack (מחסנית)Stack<T> הוא אוסף מסוג "אחרון נכנס, ראשון יוצא": האיבר שנוסף אחרון יוצא ראשון. למדו את Push, Pop ו-Peek, את החריגה של מחסנית ריקה ואת TryPop, למה מחסנית עוברת על האיברים בסדר הפוך, ושני שימושים קלאסיים: היסטוריית ביטול ובדיקת איזון סוגריים.
- Tuplestuple ב-C# מקבץ כמה ערכים לערך אחד בלי להצהיר על טיפוס. למדו את התחביר של value tuple, איברים עם שמות, החזרת כמה ערכים ממתודה, פירוק עם var (a, b), מפתחות tuple במילון, ובמה ValueTuple שונה מ-System.Tuple הישן.
- LINQLINQ מוסיף אופרטורי שאילתה לכל אוסף ב-C#. למדו את תחביר המתודות ואת תחביר השאילתות, סינון עם Where, הטלה עם Select, מיון עם OrderBy ו-ThenBy, קיבוץ עם GroupBy, המלכודת של First מול FirstOrDefault, פונקציות צבירה, ואיך ביצוע נדחה משנה את הרגע שבו השאילתה רצה.
- IEnumerable ו-yieldIEnumerable<T> הוא הממשק שמאחורי foreach ו-LINQ, ו-yield return היא הדרך הקלה ביותר לממש אותו. למדו איך IEnumerable קשור ל-ICollection ול-List, איך מתודת iterator רצה צעד אחרי צעד, yield break, רצפים אינסופיים, והמלכודות של הערכה עצלה.
מחלקות וטיפוסים
- מחלקות ואובייקטיםאיך מצהירים על מחלקה ב-C#, נותנים לה שדות ומתודות, יוצרים אובייקטים עם new, ומה המשמעות של כך שמחלקה היא טיפוס הפניה: אובייקטים משותפים, null ו-NullReferenceException, ToString ו-object initializers.
- בנאיםאיך בנאים עובדים ב-C#: בנאי ברירת המחדל המרומז ומתי הוא נעלם, בנאים עם פרמטרים והעמסת בנאים, שרשור עם this(...) ו-base(...), הסדר שבו הדברים רצים, בנאים סטטיים ופרטיים, ו-primary constructors של C# 12.
- מאפיינים (Properties)איך עובדים מאפיינים (properties) ב-C#: מאחזרי get ו-set מעל שדה גיבוי, מאפיינים אוטומטיים, private set ומאפייני קריאה בלבד, מאפיינים מחושבים, אימות ב-setter, ומילות המפתח init ו-required מ-C# 9 ו-11.
- מילת המפתח staticמה המשמעות של static ב-C#: איברים ששייכים לטיפוס ולא לאובייקט, מצב משותף בין מופעים, מתודות סטטיות ומחלקות סטטיות, בנאים סטטיים, const מול static readonly, using static, והשגיאה CS0120.
- מגדירי גישהששת מגדירי הגישה של C# (public, private, protected, internal, protected internal, private protected), מה כל אחד מהם מתיר, מה ברירת המחדל כשלא כותבים אף אחד, ואילו שגיאות קומפילציה הם מייצרים.
- ירושהאיך עובדת ירושה של מחלקות ב-C#: גזירה עם נקודתיים, מה עובר בירושה ומה לא, קריאה למחלקת הבסיס עם base, virtual ו-override, הסתרה עם new (והפלט שמפתיע את כולם), sealed, פולימורפיזם, והמרה למעלה ולמטה בהיררכיה.
- מחלקות אבסטרקטיותמהי מחלקה אבסטרקטית ב-C#: מחלקת בסיס שאי אפשר ליצור ממנה מופע, ושיכולה להצהיר על איברים אבסטרקטיים שהמחלקות היורשות חייבות לממש. כולל מתודות ומאפיינים אבסטרקטיים, בנאים, תבנית template method ומתי עדיף לבחור ב-interface.
- ממשקיםאיך עובדים ממשקים ב-C#: הצהרה על ממשק, מימוש שלו במחלקות וב-structs, שימוש בממשק כטיפוס, מימוש של כמה ממשקים בבת אחת, מימוש מפורש, ממשקי ה-framework שתממשו הכי הרבה (IComparable<T>, IEnumerable<T>, IDisposable), ומתודות ברירת מחדל בממשקים מ-C# 8.
- Structsאיך structs עובדים ב-C#: סמנטיקת ערך והעתקה בהשמה, שגיאת השינוי ב-List<T>, כללי בנאים, שוויון, boxing, readonly struct ו-record struct, והשוואה בין struct ל-class עם הנחיות מתי struct הוא הבחירה הנכונה.
- Enumsאיך enums עובדים ב-C#: הצהרה על קבועים עם שמות, ערכים שלמים בבסיס והמרות, המרה של enum למחרוזת ושל מחרוזת ל-enum עם Parse ו-TryParse, רשימת כל הערכים, [Flags] עם אופרטורים ביטיים ו-HasFlag, switch על enum, וטיפול בערכים לא מוגדרים.
- Recordsמה הם records ב-C# (מ-C# 9 ואילך): תחביר מיקום, האיברים שהקומפיילר יוצר, שוויון לפי ערך, עותקים לא הורסים עם with, ה-ToString המובנה, record struct מ-C# 10, ירושה בין records, והמחלקה המקבילה כשכותבים אותה ביד.
- Genericsאיך עובדים generics ב-C#: כתיבת מחלקות ומתודות גנריות עם פרמטרי טיפוס, הסקת טיפוסים, אילוצים עם where (class, struct, new(), מחלקות בסיס, ממשקים), default(T), איברים סטטיים לכל טיפוס, ו-covariance עם IEnumerable<out T>.
- טיפוסי Nullableכל מה שצריך לדעת על null ב-C#: טיפוסי ערך nullable (int?, Nullable<T>) עם HasValue, Value ו-GetValueOrDefault, האופרטורים ?? ו-??=, האופרטור ?., בדיקות null וטיפוסי הפניה nullable (string?) מ-C# 8.
- Pattern Matchingאיך עובד pattern matching ב-C#: האופרטור is עם תבניות טיפוס וקבוע, הוראות switch עם תבניות ב-case ו-when, ביטויי switch, תבניות מאפיינים, tuple ומיקום, תבניות יחס ולוגיקה (and, or, not), תבניות רשימה, ובאיזו גרסה של C# נוספה כל אחת.
חריגות
- Try Catchtry, catch ו-finally הם הדרך שבה C# מטפלת בחריגות. למדו איך חריגה עולה במחסנית הקריאות, איך תופסים טיפוסי חריגות ספציפיים בסדר הנכון, מסננים עם when, מנקים ב-finally, זורקים מחדש עם throw; בלי לאבד את ה-stack trace, ואילו חריגות לא לתפוס.
- זריקת חריגותאיך ומתי זורקים חריגות ב-C#. למדו את פקודת throw, איזה טיפוס חריגה מובנה מתאים לאיזו טעות, ביטויי throw עם ?? ו-?:, מתודות העזר ThrowIfNull, ואיך כותבים מחלקת חריגה מותאמת עם נתונים משלה וחריגה פנימית.
- פקודת usingפקודת using מבטיחה ש-Dispose ייקרא על קבצים, זרמים, חיבורים ואובייקטים אחרים מסוג IDisposable, גם כשנזרקת חריגה. למדו למה היא מתקמפלת, באיזה סדר האובייקטים משוחררים, את הצהרת ה-using של C# 8, ואיך לממש IDisposable במחלקה שלכם.
אסינכרוניות ותהליכונים
- Async Awaitasync ו-await מאפשרים לקוד C# לחכות לפעולות איטיות בלי לחסום thread. למדו איך מתודה async רצה, Task ו-Task<T>, הרצה של פעולות בלתי תלויות בו-זמנית עם Task.WhenAll, חריגות בקוד אסינכרוני, ולמה async void ו-.Result גורמים לבאגים ול-deadlocks.
- TasksTask היא יחידת העבודה האסינכרונית והמקבילית ב-.NET. למדו את Task.Run לעבודה שתלויה במעבד, קריאת תוצאות, שילוב משימות עם WhenAll ו-WhenAny, המשכים, ביטול עם CancellationToken, במה Task שונה מ-Thread, ומתי Parallel.For מתאים יותר.
- Lockההוראה lock מאפשרת רק לתהליכון אחד בכל רגע להריץ בלוק קוד. ראו איך תנאי מרוץ משבש מונה, תקנו אותו עם lock, למדו על איזה אובייקט לנעול, השתמשו ב-Interlocked למונים פשוטים, הימנעו מ-deadlock שנובע מסדר הנעילות, והשתמשו ב-SemaphoreSlim כשהקוד בפנים מבצע await.
- Timerל-C# יש כמה מחלקות טיימר והן מתנהגות אחרת. למדו מתי להשתמש ב-System.Threading.Timer, ב-System.Timers.Timer, ב-PeriodicTimer או בטיימר של ממשק משתמש, איך מפעילים ועוצרים אותם, למה callbacks רצים על מאגר התהליכונים ויכולים לחפוף, למה חייבים לשמור הפניה לטיימר, ואיך מודדים זמן שעבר עם Stopwatch.
קבצים, JSON ועוד
- קריאה וכתיבה של קבציםאיך כותבים לקובץ וקוראים קובץ ב-C#: File.WriteAllText, ReadAllText, AppendAllText, WriteAllLines ו-ReadAllLines לקבצים שלמים, StreamWriter ו-StreamReader לקבצים גדולים, נתיבים עם Path.Combine, תיקיות, קידודים, והחריגות שקוד שעובד עם קבצים צריך לטפל בהן.
- JSONאיך עובדים עם JSON ב-C# בעזרת System.Text.Json: JsonSerializer.Serialize ו-Deserialize, פלט ב-camelCase ועם הזחה, attributes כמו JsonPropertyName ו-JsonIgnore, enums כמחרוזות, קריאת JSON בלי מחלקות עם JsonDocument ו-JsonNode, שגיאות, והשוואה ל-Newtonsoft.Json.
- Regexאיך משתמשים בביטויים רגולריים ב-C# עם System.Text.RegularExpressions: IsMatch, Match ו-Matches, קבוצות ממוספרות וקבוצות עם שם, Replace עם החלפות ועם למבדה, Split, RegexOptions, מחרוזות verbatim לתבניות, אימות קלט, זמני קצוב, והתכונה GeneratedRegex של .NET 7.
- Reflection ו-Attributesאיך עובד reflection ב-C#: אובייקטי Type מ-typeof ומ-GetType, קריאה והצבה של מאפיינים, קריאה למתודות לפי שם, יצירת מופעים, ותכונות (attributes): המובנות כמו Obsolete, הצהרה על תכונות משלכם וקריאתן בזמן ריצה. ובנוסף, כמה reflection עולה.