static מסמן איבר ששייך לטיפוס, לא לאובייקט. שדה מופע קיים פעם אחת לכל אובייקט; שדה סטטי קיים פעם אחת, וזהו. מתודת מופע רצה על אובייקט (order.Ship()); מתודה סטטית רצה על הטיפוס (Math.Round(x)) ולא צריכה אובייקט בכלל.
שדות סטטיים: עותק אחד, משותף
הדרך הברורה ביותר לראות את ההבדל היא מונה. לכל Order יש Id משלו, אבל ספירת ההזמנות שנוצרו עד עכשיו חייבת לשבת במקום משותף.
פלט:
Ana #1, Ben #2, Chen #3
Orders created: 3
ניגשים לאיברים סטטיים דרך שם הטיפוס: Order.Created, לא a.Created. C# לא מאפשרת להגיע לאיבר סטטי דרך מופע (שגיאה CS0176), וכך ברור במקום הקריאה שהערך משותף.
מתודות סטטיות
למתודה סטטית אין this. היא עובדת רק עם הפרמטרים שלה ועם איברים סטטיים אחרים. זה הופך אותה לצורה הטבעית לפונקציות עזר שמחשבות משהו מהקלטים שלהן:
פלט:
61.38 EUR
7
גם Main עצמה סטטית: סביבת הריצה קוראת לה לפני שקיים אובייקט כלשהו.
השגיאה CS0120: קוד סטטי שמשתמש באיברי מופע
מכיוון ש-Main סטטית, מתחילים נתקלים לעיתים קרובות בשגיאה הזו ברגע שהם מוסיפים מתודה שנייה:
class Program
{
int visits;
void Greet() { Console.WriteLine("Hello"); }
static void Main()
{
Greet(); // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
visits++; // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
}
}
למתודה סטטית אין אובייקט, ולכן אין לה visits להגדיל. יש שני תיקונים, והנכון תלוי במה שהאיבר מייצג:
- האיבר באמת הוא נתון של אובייקט: צרו אובייקט והשתמשו בו.
var app = new Program(); app.Greet(); - האיבר לא צריך מצב של אובייקט: סמנו אותו
static.static void Greet() { ... }
סימון של הכול כ-static כדי שהשגיאה תיעלם עובד בתוכנית זעירה והופך למצב גלובלי בתוכנית אמיתית. החליטו לפי השאלה אם הנתונים שייכים לאובייקט אחד או לכל התוכנית.
הכיוון ההפוך תמיד מותר: מתודות מופע יכולות לקרוא לאיברים סטטיים בחופשיות.
מחלקות סטטיות
מחלקה שמוצהרת static יכולה להכיל רק איברים סטטיים, ואי אפשר ליצור ממנה מופע אף פעם. היא קבוצה של פונקציות וקבועים עם שם.
פלט:
77
100
True
הקומפיילר אוכף את החוזה: איבר מופע בתוך מחלקה סטטית הוא שגיאה CS0708, ו-new עליה הוא שגיאה CS0712. Math, Console, File, Path ו-Enumerable הן כולן מחלקות סטטיות. גם מתודות הרחבה חייבות לשבת במחלקה סטטית.
בנאים סטטיים ו-static readonly
שדה סטטי שהערך שלו צריך יותר מביטוי אחד אפשר לאתחל ב-בנאי סטטי, שסביבת הריצה מריצה פעם אחת, לפני השימוש הראשון בטיפוס:
פלט:
Before first use
ShippingRates initialized
5.50
3
2026-01-01
readonly אומר שאפשר להציב ערך בשדה רק באתחול שלו או בבנאי הסטטי. אחרי זה אי אפשר להציב בו ערך חדש, אם כי האובייקט שהוא מצביע עליו עדיין יכול להיות ניתן לשינוי, ולכן המילון נחשף כ-IReadOnlyDictionary.
const מול static readonly
שניהם נותנים ערך עם שם שמשותף לכל התוכנית, והם נבדלים ברגע שבו הערך נקבע:
const | static readonly | |
|---|---|---|
| הערך נקבע | בזמן קומפילציה | בזמן ריצה, פעם אחת |
| טיפוסים מותרים | מספרים, char, bool, string, enums, null | כל טיפוס |
| סטטי במרומז | כן (אי אפשר לכתוב static const) | מוצהר סטטי במפורש |
| מועתק ל-assemblies הקוראים | כן | לא, נקרא מהשדה |
שמיש ב-cases של switch ובארגומנטים של תכונות | כן | לא |
שורת ההעתקה חשובה בספריות: אם assembly A מצהיר על public const int MaxItems = 50; ו-assembly B משתמש בו, B מכיל את הליטרל 50. שינוי של A ל-100 לא עושה כלום ל-B עד ש-B מקומפל מחדש. לערכים שעשויים להשתנות בין גרסאות, העדיפו static readonly.
using static
הנחיית using static (C# 6) מייבאת את האיברים הסטטיים של טיפוס, כך שאפשר לקרוא להם בלי שם הטיפוס:
פלט:
Area: 28.27
40
זה נקרא טוב בקוד עתיר חישובים מתמטיים. במקומות אחרים השתמשו בזה במשורה: WriteLine או Parse חשופים מסתירים מאיזה טיפוס המתודה מגיעה.
מצב סטטי ותהליכונים
שדה סטטי משותף לכל התהליכונים בתהליך. הגדלה שלו מכמה תהליכונים בבת אחת מאבדת עדכונים, כי created++ הוא קריאה, חיבור וכתיבה, ושני תהליכונים יכולים לשזור אותם זה בזה. השתמשו ב-Interlocked.Increment(ref created) למונים, או ב-lock לכל דבר גדול יותר. אפליקציות web הן המקום הרגיל שבו זה נושך: כל בקשה רצה על תהליכון כלשהו, כך ששדה סטטי שמחזיק את "המשתמש הנוכחי" משותף לכל המשתמשים.
עוד דקות אחת: במחלקה גנרית, כל טיפוס שנבנה מקבל שדות סטטיים משלו. Cache<int>.Count ו-Cache<string>.Count הם שני שדות שונים.
מתי להשתמש ב-static
שימושים טובים:
- פונקציות עזר טהורות שמחשבות תוצאה מהארגומנטים שלהן: עיצוב, המרות, אימות.
- קבועים ונתוני חיפוש לקריאה בלבד שמשותפים לתוכנית.
- מתודות factory, כמו
TimeSpan.FromMinutes(5)אוTemperature.FromCelsius(21)משלכם. - מתודות הרחבה.
שימושים גרועים:
- מצב גלובלי שניתן לשינוי (המשתמש הנוכחי, עגלת קניות, הגדרות שמשתנות). הוא מצמיד כל חלק קוד שנוגע בו והופך בדיקות לתלויות בשאריות של בדיקות אחרות.
- כל דבר שתרצו להחליף בבדיקות, כמו שעון, מסד נתונים או שולח אימיילים. אי אפשר להחליף קריאות סטטיות במימוש מזויף; מופע שמועבר לבנאי אפשר.
שאלות נפוצות
מה המשמעות של static ב-C#?
איבר static שייך לטיפוס עצמו ולא לאובייקט כלשהו. יש עותק אחד של שדה סטטי, לא משנה כמה אובייקטים קיימים, ולמתודה סטטית קוראים על שם הטיפוס (Math.Max(3, 7)) בלי ליצור מופע. למתודה סטטית אין this, ולכן היא לא יכולה להשתמש ישירות בשדות מופע.
מה זו מחלקה סטטית ב-C#?
מחלקה שמוצהרת static יכולה להכיל רק איברים סטטיים, ואי אפשר ליצור ממנה מופע אף פעם (new עליה היא שגיאה CS0712). היא מקבצת פונקציות קשורות שלא צריכות מצב של אובייקט, כמו Math, Console או File. מתודות הרחבה חייבות להיות מוצהרות במחלקה סטטית.
איך מתקנים את השגיאה "An object reference is required for the non-static field, method, or property"?
זו שגיאה CS0120: מתודה סטטית, לעיתים קרובות Main, משתמשת באיבר מופע. או שיוצרים אובייקט וקוראים לאיבר עליו (var app = new Program(); app.Run();), או שמסמנים את האיבר כ-static אם הוא לא צריך נתונים של אובייקט מסוים.
מה ההבדל בין const ל-static readonly ב-C#?
const הוא קבוע בזמן קומפילציה (מספרים, מחרוזות, bool, enums) שהערך שלו מועתק לכל assembly שמשתמש בו. שדה static readonly מקבל ערך פעם אחת בזמן ריצה, באתחול שלו או בבנאי הסטטי, ויכול להחזיק כל טיפוס, כמו DateTime או רשימה. השתמשו ב-const לערכים שלעולם לא ישתנו, וב-static readonly לכל השאר.
מתי להשתמש ב-static ב-C#?
השתמשו ב-static לקוד שעובד רק על הפרמטרים שלו (פונקציות עזר טהורות כמו מעצב מחירים), לקבועים ולמטמונים שבאמת גלובליים לתוכנית, ולמתודות factory. הימנעו מ-static בכל דבר שמחזיק מצב של משתמש או של בקשה, או שתרצו להחליף בבדיקות: מצב משותף שניתן לשינוי קשה לבדיקה ואינו בטוח בין תהליכונים בלי נעילה.