# Properties ו-Access Modifiers📖 שיעור

## מה זה?

בשיעור הקודם השדות היו `public` — כל קוד חיצוני יכול לשנות אותם בלי שום בקרה. מה מונע `bankAccount.Balance = -500;`? כלום — וזו בעיה אמיתית: יתרת חשבון בנק שלילית לא אמורה להיות אפשרית. Properties פותרות את זה: נותנות שליטה מלאה על מה קורה כשקוראים או משנים ערך, תוך שמירה על התחביר הנוח של גישה ישירה (`account.Balance`, לא `account.GetBalance()`).

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

• Property — "שדה חכם": נראה כמו שדה רגיל בגישה (`obj.Name`), אבל בפועל מריץ קוד (`get`/`set`) מאחורי הקלעים

• `get` — הקוד שרץ כשקוראים לערך של ה-Property

• `set` — הקוד שרץ כשמנסים לשנות את הערך של ה-Property; יכול לדחות ערכים לא חוקיים

• Access Modifier — קובע מי יכול לגשת לחבר מסוים: `public` (כולם), `private` (רק בתוך המחלקה עצמה), `protected` (המחלקה ויורשיה)

• Auto-Property — Property מקוצרת בלי לוגיקה מותאמת אישית, כשה-`get`/`set` רק "מעבירים" ערך כרגיל

```csharp
class BankAccount
{
    private double _balance;   // private — accessible only within the class itself

    public double Balance
    {
        get { return _balance; }
        set
        {
            if (value < 0)
                throw new ArgumentException("Balance cannot be negative");
            _balance = value;
        }
    }
}

BankAccount account = new BankAccount();
account.Balance = 500;    // valid — goes through the set
// account.Balance = -100;  // throws ArgumentException
```

```csharp
// Auto-Property — when there's no special logic, no need for a separate manual field
class Student
{
    public string Name { get; set; }           // freely readable and writable
    public int Age { get; private set; }       // readable from outside, but only writable inside the class

    public Student(string name, int age)
    {
        Name = name;
        Age = age;   // valid — this.Age = age inside the class itself
    }
}
```

## הסבר עיקרי

למה `private set` שימושי — `{ get; private set; }` אומר: "כל אחד יכול לקרוא את `Age`, אבל רק קוד בתוך `Student` עצמה יכול לשנות אותו" — למשל רק בקונסטרוקטור, או במתודה ייעודית כמו `Birthday()`. זה מונע מקוד חיצוני "לשנות גיל תלמיד" ישירות באופן שרירותי, בלי לחסום קריאה חופשית שלו.

Property מול שדה רגיל — מבחוץ, `account.Balance = 500` נראה **בדיוק** אותו דבר בין אם `Balance` הוא שדה או Property. ההבדל הוא ש-Property מריצה קוד משלה (`set`) בכל פעם — כאן: בדיקת תקינות לפני שמירה. זו הסיבה ש-C# ממליצה על Properties (לא שדות `public`) לכל דבר שנגיש מחוץ למחלקה.

Access Modifiers — `private` הוא ברירת המחדל הבטוחה: פרט מימוש פנימי שאין סיבה שקוד חיצוני יגע בו ישירות (כמו `_balance` בדוגמה). `public` נחשף החוצה במפורש כשזה חלק מה-API של המחלקה. `protected` (יילמד בשיעור הירושה) נמצא באמצע — נגיש למחלקה ולכל מה שיורש ממנה.

## יתרונות

Properties מאפשרות ולידציה ולוגיקה בכל גישה/שינוי, בלי לשנות את התחביר בצד הקורא; `private set` מגן על שדות מפני שינוי חיצוני בלתי-רצוי בלי לחסום קריאה; Auto-Properties נותנות תחביר קצר כשאין צורך בלוגיקה מיוחדת.

## חסרונות

Property עם לוגיקה כבדה מדי ב-`get` (למשל קריאת רשת) עלולה להטעות קוד קורא שמצפה לגישה מהירה כמו שדה רגיל; יותר מדי Properties עם `set` פתוח לכולם מאבד את היתרון של הגנה על מצב פנימי.

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

• Property = "שדה חכם" עם `get`/`set` — נראה כמו שדה, מריץ קוד מאחורי הקלעים

• `private` = נגיש רק בתוך המחלקה; `public` = נגיש מכל מקום; `protected` = המחלקה ויורשיה

• `{ get; private set; }` מאפשר קריאה חופשית אך שינוי רק מבפנים

• Auto-Property (`{ get; set; }` בלי גוף) היא קיצור כשאין לוגיקה מיוחדת נדרשת

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

• להשאיר שדות `public` בלי Properties — מאבד כל אפשרות לולידציה או בקרה עתידית

• להשתמש ב-`set` פתוח לכולם על שדה שאמור להשתנות רק בתנאים מבוקרים (כמו יתרת חשבון)

• לשכוח שקריאה ל-Property (`get`) יכולה בעצמה להריץ לוגיקה — לא תמיד "בחינם" כמו שדה רגיל

## סיכום

Properties נותנות "שדה חכם" — תחביר גישה זהה לשדה רגיל (`obj.Name`), אבל עם אפשרות להריץ לוגיקה (ולידציה, חישוב) בכל `get`/`set`. Access Modifiers (`private`/`public`/`protected`) קובעים מי יכול לגשת לכל חבר במחלקה — ברירת המחדל הבטוחה היא `private`, וחושפים `public` רק את מה שבאמת צריך להיות חלק מה-API החיצוני.

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

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

[Microsoft Learn — Access modifiers](https://learn.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers)

---

## תרגילים

### תרגיל 1 — Property עם ולידציה

**המשימה:** הוסיפו למחלקה `Student` (משיעור קודם) Property בשם `Age` עם `set` שדוחה גילאים שליליים (זורק `ArgumentException`).

**בדיקה:** `student.Age = 20` עובד; `student.Age = -5` זורק חריגה.

### תרגיל 2 — private set

**המשימה:** הפכו את `BankAccount.Balance` ל-`{ get; private set; }`, והוסיפו מתודות `Deposit(double amount)` ו-`Withdraw(double amount)` שהן היחידות שיכולות לשנות את היתרה.

**בדיקה:** ניסיון לכתוב `account.Balance = 1000;` מחוץ למחלקה גורם לשגיאת קומפילציה; `account.Deposit(1000)` עובד ומעדכן את היתרה כראוי.

---

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

**המשימה:** בנו מחלקה `Product` עם ניהול מלאי מוגן.

**דרישות:**
1. Properties: `Name` (`{ get; set; }` חופשי), `Price` (`get` חופשי, `set` דוחה ערכים שליליים), `Stock` (`get` חופשי, `private set`)
2. מתודות `AddStock(int amount)` ו-`RemoveStock(int amount)` — `RemoveStock` דוחה הסרה של יותר מהמלאי הקיים (זורקת `InvalidOperationException`)
3. הדגימו: יצירת מוצר, הוספת מלאי, הסרת מלאי תקינה, וניסיון הסרה שחורג מהמלאי (תפוס עם `try`/`catch`)

**בדיקה:** ניסיון `product.Price = -10` זורק חריגה; `RemoveStock` שחורג מהמלאי הקיים זורק `InvalidOperationException` ותפוס בהצלחה בקוד הקורא.

## מה בפרק הבא

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