# Value מול Reference, ו-out📖 שיעור

## מה זה?

שינוי פרמטר בתוך פונקציה — האם זה משפיע על המשתנה המקורי בחוץ, או לא? התשובה תלויה בטיפוס: `int` (Value Type) שנשלח לפונקציה מקבל **עותק** — שינוי בפנים לא משפיע בחוץ. אבל אובייקט (Reference Type) שנשלח לפונקציה משתף את אותה הפניה — שינוי **בתוך** האובייקט (לא החלפתו לגמרי) כן ישפיע בחוץ. זה ההבדל שכבר נרמז בשיעור על טיפוסים פרימיטיביים מול Object — עכשיו רואים אותו בפעולה.

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

• Value Type — טיפוס שמועבר כ**עותק** של הערך (`int`, `double`, `bool`, `char`, `struct`)

• Reference Type — טיפוס שמועבר כ**הפניה** (reference) לאותו אובייקט בזיכרון (`string`, מחלקות, `List<T>`)

• `out` — מילת מפתח שגורמת לפרמטר להיות "יציאה" — הפונקציה **חייבת** לתת לו ערך, וההשפעה נראית בחוץ

```csharp
// Value Type — copy, changing it inside does not affect the outside
static void TryDouble(int number)
{
    number = number * 2;   // affects only the local copy
}

int x = 5;
TryDouble(x);
Console.WriteLine(x);   // still 5 — the function only changed a copy
```

```csharp
// Reference Type — changing inside the object does affect the outside
static void AddItem(List<string> list)
{
    list.Add("new");   // changes the shared object itself
}

List<string> items = new List<string> { "a", "b" };
AddItem(items);
Console.WriteLine(items.Count);   // 3 — the shared object changed
```

```csharp
// out — when you want a function to "return" more than one value
static bool TryDivide(int a, int b, out int result)
{
    if (b == 0)
    {
        result = 0;
        return false;
    }
    result = a / b;
    return true;
}

if (TryDivide(10, 2, out int answer))
{
    Console.WriteLine($"The result: {answer}");   // 5
}
```

## הסבר עיקרי

למה `int` לא מושפע אבל `List<string>` כן — כשמעבירים `int x` לפונקציה, C# מעתיקה את **הערך** (`5`) לפרמטר המקומי — זה משתנה נפרד לגמרי מ-`x` המקורי. כשמעבירים `List<string> items`, C# מעתיקה רק את ה**הפניה** (הכתובת) לאותו אובייקט ברשימה — הפרמטר המקומי מצביע לאותו אובייקט ממש, כך ש-`list.Add(...)` משנה את האובייקט שגם `items` בחוץ מצביע אליו.

ההבדל בין "לשנות את האובייקט" ל"להחליף את ההפניה" — חשוב להבחין: `list.Add("חדש")` משנה את **תוכן** האובייקט הקיים — זה נראה בחוץ. אבל `list = new List<string>()` (בתוך הפונקציה) רק מחליף למה **הפרמטר המקומי** מצביע — לא משפיע על `items` בחוץ, כי זו רק החלפת ההפניה המקומית, לא שינוי האובייקט המשותף.

מתי `out` נחוץ — פונקציה רגילה יכולה להחזיר רק ערך אחד עם `return`. `out` מאפשר "להחזיר" גם דרך פרמטר — שימושי כש-`TryParse`/`TryDivide` צריכים גם תוצאת בוליאן (הצליח/לא) וגם את הערך המחושב עצמו, בלי לבנות אובייקט מיוחד רק בשביל זה.

## יתרונות

Value Types מגנים מפני שינוי בלתי-צפוי — פונקציה לא יכולה "לזיין" משתנה `int` שנשלח אליה; Reference Types חוסכים העתקה יקרה של מבני נתונים גדולים בכל קריאת פונקציה; `out` מאפשר "החזרת" כמה ערכים בלי ליצור מחלקה מיוחדת רק לשם כך.

## חסרונות

ההבחנה Value/Reference יכולה להפתיע מפתחים מתחילים — שינוי בתוך פונקציה עובד "אחרת" לגבי אובייקטים לעומת פרימיטיבים; `out` שובר קריאות אם משתמשים בו יותר מדי — פונקציה עם כמה פרמטרי `out` קשה לקרוא בנקודת הקריאה.

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

• Value Type מועבר כעותק; Reference Type מועבר כהפניה לאותו אובייקט

• שינוי **תוכן** אובייקט (Reference) בתוך פונקציה נראה בחוץ; החלפת ההפניה עצמה — לא

• `out` מאפשר לפונקציה "להחזיר" ערך נוסף דרך פרמטר, בנוסף ל-`return`

• `TryParse`/`TryDivide` הם השימוש הכי נפוץ ב-`out` — הצלחה בוליאנית + תוצאה

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

• לצפות ששינוי `int`/`double` בתוך פונקציה ישפיע על המשתנה המקורי בחוץ

• לבלבל בין "לשנות אובייקט" (`list.Add`, נראה בחוץ) ל"להחליף הפניה" (`list = new List<>()`, לא נראה בחוץ)

• לשכוח לתת ערך לפרמטר `out` בכל נתיב קוד אפשרי — שגיאת קומפילציה

## סיכום

Value Types (`int`, `double`, `bool` וכו') מועברים לפונקציות כעותק — שינוי בפנים לא משפיע בחוץ. Reference Types (`string`, מחלקות, `List<T>`) מועברים כהפניה — שינוי **בתוך** האובייקט המשותף כן נראה בחוץ, אך החלפת ההפניה עצמה לא. `out` מאפשר לפונקציה "להחזיר" ערך נוסף דרך פרמטר, מעבר ל-`return` הרגיל.

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

[Microsoft Learn — Value types and reference types](https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/value-types)

[Microsoft Learn — out parameter modifier](https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/out-parameter-modifier)

---

## תרגילים

### תרגיל 1 — Value Type לא משתנה

**המשימה:** כתבו פונקציה `void AddTen(int number)` שמנסה להוסיף 10 לפרמטר, וקראו לה על משתנה `int`. הדפיסו את המשתנה לפני ואחרי הקריאה.

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

### תרגיל 2 — Reference Type כן משתנה

**המשימה:** כתבו פונקציה `void AddItem(List<int> numbers)` שמוסיפה את המספר `99` לרשימה שהתקבלה, וקראו לה על `List<int>` קיימת. הדפיסו את מספר האיברים לפני ואחרי.

**בדיקה:** מספר האיברים גדל ב-1 אחרי הקריאה — הרשימה המקורית השתנתה.

---

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

**המשימה:** כתבו פונקציה `bool TryGetDiscount(double price, string couponCode, out double finalPrice)` שמחשבת מחיר אחרי הנחה.

**דרישות:**
1. אם `couponCode` שווה ל-`"SAVE10"` — `finalPrice` = `price * 0.9`, הפונקציה מחזירה `true`
2. עבור כל קוד קופון אחר — `finalPrice` = `price` (ללא שינוי), הפונקציה מחזירה `false`
3. ב-`Main`, קראו לפונקציה עם קוד קופון תקין ולא תקין, והדפיסו הודעה מתאימה לכל מקרה בעזרת ה-`bool` וה-`out` שהתקבלו

**בדיקה:** קופון `"SAVE10"` על מחיר `100` מחזיר `true` ו-`finalPrice = 90`; קופון לא קיים מחזיר `false` ו-`finalPrice` שווה למחיר המקורי.

## מה בפרק הבא

בפרק הבא — **Nullable Types**: איך מייצגים "אין ערך" גם עבור טיפוס פרימיטיבי כמו `int`, שבברירת מחדל תמיד חייב להכיל מספר כלשהו.
