# פונקציות (מתודות)📖 שיעור

## מה זה?

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

## מילות מפתח שחשוב לזכור

• Method — בלוק קוד עם שם, שאפשר לקרוא לו (Call) מכמה מקומות בתוכנית

• Parameter — משתנה שהפונקציה מקבלת כקלט, מוגדר בחתימה שלה

• Return Value — הערך שהפונקציה מחזירה החוצה אחרי הריצה, עם `return`

• `void` — טיפוס החזרה שמציין שהפונקציה לא מחזירה שום ערך

• Overloading — כמה מתודות עם אותו שם אך פרמטרים שונים (מספר/טיפוס)

## בונים פונקציה שלב אחר שלב

### שלב 1: המצב בלי פונקציות — קוד מועתק-מודבק

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

```csharp
// במקום 1 — הדפסת תעודה
int math = 80, english = 90, science = 70;
double avg1 = (math + english + science) / 3.0;
Console.WriteLine($"ציון תעודה: {avg1}");

// במקום 2 — בדיקת מלגה
int math2 = 65, english2 = 72, science2 = 88;
double avg2 = (math2 + english2 + science2) / 3.0;   // אותו חישוב, שוב
if (avg2 >= 70)
    Console.WriteLine("זכאי למלגה");

// במקום 3 — דוח סיכום
int math3 = 95, english3 = 60, science3 = 75;
double avg3 = (math3 + english3 + science3) / 3.0;   // ושוב
Console.WriteLine($"ממוצע כיתתי לדוגמה: {avg3}");
```

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

### שלב 2: אורזים את החישוב לבלוק אחד עם שם — Method

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

```csharp
static double CalculateAverage(int a, int b, int c)
{
    return (a + b + c) / 3.0;
}
```

בואו נעצור על השורה הראשונה, מילה-מילה:

- `static` — כרגע, בכל התרגילים שלנו, המתודות שייכות ישירות לתוכנית (לא למופע של מחלקה) — `static` אומר "אפשר לקרוא למתודה הזו בלי ליצור אובייקט קודם", בדיוק כמו ש-`Main` עצמה מוגדרת `static`. (ביחידת ה-OOP נראה גם מתודות בלי `static`, ששייכות לאובייקט ספציפי.)
- `double` — טיפוס ההחזרה: הערך שהמתודה מחזירה בסוף יהיה מסוג `double` (מספר עשרוני).
- `CalculateAverage` — שם המתודה, שבו נקרא לה מאוחר יותר.
- `(int a, int b, int c)` — שלושה **Parameters**: משתנים שהמתודה מקבלת כקלט. כל אחד מוגדר עם טיפוס (`int`) ושם (`a`, `b`, `c`) — בדיוק כמו הגדרת משתנה רגילה, רק שהערך שלהם יגיע מבחוץ, מהקריאה למתודה.
- `{ return (a + b + c) / 3.0; }` — גוף המתודה. `return` הוא המילה שאומרת "זה הערך שחוזר החוצה" — וברגע שהקוד מגיע אליה, המתודה **מפסיקה לרוץ מיד**, גם אם יש שורות אחריה.

### שלב 3: קוראים למתודה — Call

עכשיו, בכל אחד משלושת המקומות משלב 1, במקום לחשב ידנית:

```csharp
double avg1 = CalculateAverage(80, 90, 70);
Console.WriteLine($"ציון תעודה: {avg1}");
```

`CalculateAverage(80, 90, 70)` היא **הקריאה** (Call) למתודה. שימו לב להבחנה חשובה: `a`, `b`, `c` בהגדרת המתודה (שלב 2) נקראים **Parameters** — הם רק השמות שהמתודה משתמשת בהם פנימית. `80`, `90`, `70` בקריאה בפועל נקראים **Arguments** — הערכים האמיתיים שנשלחים. C# מתאימה אותם לפי הסדר: `80` → `a`, `90` → `b`, `70` → `c`.

עכשיו, אם צריך לעגל את הממוצע — משנים **שורה אחת בלבד**, בתוך `CalculateAverage`, וכל שלושת מקומות הקריאה מקבלים את התיקון אוטומטית:

```csharp
static double CalculateAverage(int a, int b, int c)
{
    return Math.Round((a + b + c) / 3.0, 2);   // שינוי אחד, במקום אחד
}
```

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

### שלב 4: מתודה שלא מחזירה כלום — void

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

```csharp
static void PrintGreeting(string name)
{
    Console.WriteLine($"Hello, {name}!");
}

PrintGreeting("Dana");   // בלי "=" — אין ערך מוחזר שאפשר לשמור במשתנה
```

`void` (פשוטו כמשמעו: "ריק") אומר "אין כאן `return` עם ערך". אפשר עדיין לכתוב `return;` בלי ערך בתוך מתודת `void`, כדי לצאת ממנה מוקדם (למשל `if (name == null) return;`) — רק אי אפשר להחזיר ערך אחריה.

### שלב 5: כמה מתודות תחת אותו שם — Overloading

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

```csharp
static double CalculateAverage(int a, int b, int c) => (a + b + c) / 3.0;
static double CalculateAverage(int[] numbers) => numbers.Sum() / (double)numbers.Length;
```

(שימו לב לתחביר `=>` — זו צורה מקוצרת לגוף בן-שורה-אחת של מתודה, זהה לגמרי ל-`{ return ...; }`.)

עכשיו `CalculateAverage(80, 90, 70)` מפעילה את הגרסה הראשונה, ו-`CalculateAverage(myArray)` מפעילה את השנייה — C# בוחרת אוטומטית לפי מה שנשלח בקריאה. הקוד שקורא לפונקציה פשוט קורא ל-`CalculateAverage`, בלי לדעת (ובלי צורך לדעת) שיש שתי גרסאות.

### טעות נפוצה: לשכוח return

```csharp
static double CalculateAverage(int a, int b, int c)
{
    double result = (a + b + c) / 3.0;
    // שכחו return result; כאן
}
```

הקוד הזה לא מתקמפל בכלל — שגיאה: `not all code paths return a value`. הסיבה: הגדרנו שהמתודה מחזירה `double` (זה מה ש-`double` בתחילת החתימה מבטיח לכל מי שקורא לה), אבל בפועל אין שום `return` שמקיים את ההבטחה הזו. C# בודקת את זה בזמן קומפילציה, לפני שהתוכנית בכלל רצה — כדי שלא תגלו את הבעיה רק כשמישהו יקרא למתודה ויצפה לערך שלא קיים.

## הסבר עיקרי

למה Overloading פותר בעיה אמיתית: בלי Overloading, היינו צריכים שמות שונים לכל גרסה (`CalculateAverageOfThree`, `CalculateAverageOfArray`) — מסורבל, ומכריח את כל מי שקורא לפונקציה לזכור איזה שם מתאים לאיזה מקרה. עם Overloading, יש שם אחד (`CalculateAverage`), ו-C# בוחרת את הגרסה הנכונה לבד לפי מספר/טיפוס הארגומנטים שנשלחו — קוד הקורא (Caller) פשוט חושב "אני רוצה ממוצע", לא "אני צריך את הגרסה השנייה".

## יתרונות

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

## חסרונות

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

## נקודות חשובות

• Parameter = הגדרה בחתימה; Argument = ערך אמיתי שנשלח בקריאה

• `void` = הפונקציה לא מחזירה ערך; כל טיפוס אחר חייב `return` בכל נתיב אפשרי בקוד

• `return` יוצא מהפונקציה מיידית — קוד אחריו באותה ריצה לא מתבצע

• Overloading = כמה מתודות באותו שם, נבדלות במספר/טיפוס הפרמטרים (לא בטיפוס ההחזרה בלבד)

## טעויות נפוצות

• לשכוח `return` בפונקציה שלא `void` — שגיאת קומפילציה "not all code paths return a value" (ראו הסבר מלא למעלה)

• לצפות שפונקציית `void` תחזיר ערך שאפשר לשמור במשתנה

• לנסות Overload רק לפי טיפוס ההחזרה בלי לשנות פרמטרים — לא חוקי ב-C#, הקומפיילר לא יודע להבדיל

## סיכום

פונקציות (מתודות) אורזות לוגיקה חוזרת לבלוק אחד עם שם, כדי שהקוד ייכתב פעם אחת ויתוקן במקום אחד. Parameters הם הקלט שהפונקציה מגדירה, Return Value הוא הפלט שהיא מחזירה, ו-`void` מציין שאין ערך מוחזר. Overloading מאפשר כמה גרסאות של אותה פעולה תחת שם אחד, כשה-Compiler בוחר את הגרסה הנכונה לפי הארגומנטים שנשלחו.

## דוקומנטציה רשמית

[Microsoft Learn — Methods](https://learn.microsoft.com/dotnet/csharp/methods)

[Microsoft Learn — Method overloading](https://learn.microsoft.com/dotnet/csharp/methods#method-signatures)

---

## תרגילים

### תרגיל 1 — פונקציה עם ערך מוחזר

**המשימה:** כתבו פונקציה `IsEven(int number)` שמחזירה `bool` — האם המספר זוגי. קראו לה על כמה מספרים והדפיסו את התוצאה.

**בדיקה:** `IsEven(4)` מחזירה `true`; `IsEven(7)` מחזירה `false`.

### תרגיל 2 — Overloading

**המשימה:** כתבו שתי גרסאות של פונקציה `Print`: אחת שמקבלת `string` ומדפיסה אותו כמו שהוא, ואחת שמקבלת `int` ומדפיסה אותו עם הקידומת `"מספר: "`.

**בדיקה:** `Print("שלום")` מדפיס `שלום`; `Print(5)` מדפיס `מספר: 5` — בלי שקוראים לפונקציות בשמות שונים.

---

## פרויקט מסכם

**המשימה:** כתבו "מחשבון פשוט" עם 4 פונקציות נפרדות: `Add`, `Subtract`, `Multiply`, `Divide`, כל אחת מקבלת שני `double` ומחזירה `double`.

**דרישות:**
1. כל פעולה היא פונקציה נפרדת עם ערך מוחזר (לא `void`)
2. `Divide` בודקת אם המכנה הוא `0` — אם כן, מדפיסה הודעת שגיאה ומחזירה `0` במקום לחשב חלוקה לא חוקית
3. בפונקציית `Main`, קראו לכל ארבע הפונקציות עם ערכים לדוגמה והדפיסו את התוצאות

**בדיקה:** `Add(5, 3)` מחזיר `8`; `Divide(10, 0)` מדפיס הודעת שגיאה ומחזיר `0` בלי לקרוס.

## מה בפרק הבא

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