בעיה מעניינת ב C#
-
using System; namespace TestProblem { class Program { static void Main(string[] args) { int i = 1; if (i == 2) { Console.WriteLine("THE CONDITION IS TRUE"); int x = TestProblem.Test; int v = TestProblem.Test; } Console.ReadKey(); } } public class TestProblem { public static int Test = TestProblem.Get(); public static int Get() { Console.WriteLine("Before"); return 0; } } }
-
אני מבין שזה קשור לאובייקט הסטטי במחלקה, שמשום מה הוא נוצר אפילו שהתנאי לא מתקיים.
-
נא לשים לב שאם התנאי היה מתקיים, היה אמור להיות כתוב: "The condition is true"
-
לשים לב שהאובייקט נקרא פעמיים, אבל הפונקציה שלא אמורה להיקרא, נקראת פעם אחת. זה בגלל שהוא סטטי. רק רציתי להעיר את תשומת הלב.
4)תנסו לכתוב במקום תנאי, ישירות את הערך false, ותיווכחו שהפונקציה לא נקראית
-
-
מחלקה סטטית לפי הגדרתה היא חד מופעית ביחס לכל האפליציה.
כלומר החברים הסטטיים של TestProblem יישארו זהים לאורך כל פעילות התוכנה.
אם כן נשאלת השאלה, מתי הם מאותחלים? התשובה היא "זה לא עניינך, רק תהיה בטוח שזה יקרה לפני הגישה בפעם הראשונה".הנה לשון התיעוד:
As is the case with all class types, the type information for a static class is loaded by the .NET runtime when the program that references the class is loaded. The program cannot specify exactly when the class is loaded. However, it is guaranteed to be loaded and to have its fields initialized and its static constructor called before the class is referenced for the first time in your program. A static constructor is only called one time, and a static class remains in memory for the lifetime of the application domain in which your program resides.
כפי שמומחש פה הבעיה התעוררה מאתחול סטטי שהוא אתחול מסוכן מאוד (ראשית בגלל העיתוי הלא צפוי ושנית כישלון שמה הוא בעצם כישלון לאורך כל התוכנית שהרי זה אורך חיי החברים הסטטיים), אתחול סטטי כדאי שישעה רק כמקרה חריג ואחרי בחינת ההשלכות.
-
@מנצפך בקריאה חוזרת של מה שכתבת אני מבין שעד עכשיו היית מצפה אחרת:
- אני מבין שזה קשור לאובייקט הסטטי במחלקה, שמשום מה הוא נוצר אפילו שהתנאי לא מתקיים.
זה ההיפך, ה"משום מה" הוא שזה קורה רק אם אתה מתייחס למחלקה (מן lazy כזה שלא טוענים מה שלא צריך) הפשטות של המושג סטטי אומרת שזה צריך לקרות ברגע טעינת האפליקציה.
-
לא הבנתי מה הולך פה, אם האובייקט הסטטי מאותחל מיד, אני אמור לראות הדפסה של Before, אבל למעשה אני לא רואה שום דבר מודפס?
-
@יוסף-בן-שמעון זה יכול להיות תלוי בגירסת ה-CLR.
תקציר הכתבה של jon skeet האגדי:
- ה-CLR תומך בשני סוגי איתחול סטטיים:
- תזמון קשיח של האתחול: בדיוק ברגע של הגישה הראשונה לכל שדה או פונקציה (סטטי או לא) של המחלקה.
- תזמון רופף (בדיקומפליזציה של המחלקה רואים שיש לו מאפיין:
beforefieldinit
), ה-CLR יכול לבחור אם ומתי לאתחל, יש רק הבטחה שזה יאותחל לפני הגישה הראשונה לשדה סטטי של המחלקה
זה אומר שבפועל האתחול יכול לקרות יותר מוקדם או יותר מאוחר מבמקרה של תזמון קשיח.
- אם יש constructor סטטי, אז לפי ה-spec של #C הקומפיילר חייב להגדיר את זה כקשיח.
עכ"ד בקיצור
במקרה של @מנצפך מכיון שאין בנאי סטטי, המחלקה מוגדרת עם המאפיין
beforefieldinit
(אפשר לראות ב-dnSpy).
מכיון שאין גישה בפועל לשדה סטטי של המחלקה (אולי לא במקרה האמיתי אבל לפחות בקוד הדוגמה), ה-CLR יכול לבחור לא לאתחל אותו בכלל, או לאתחל אותו בכל מקום בתוכנה שבא לו... - ה-CLR תומך בשני סוגי איתחול סטטיים:
-
@יוסף-בן-שמעון אמר בבעיה מעניינת ב C#:
אבל למעשה אני לא רואה שום דבר מודפס?
@yossiz אמר בבעיה מעניינת ב C#:
@יוסף-בן-שמעון זה יכול להיות תלוי בגירסת ה-CLR.
מתברר שבדוטנט core הבנאי הסטטי לא נקרא בכלל, ובדוטנט רגיל זה כן נקרא