הקדמה
בסיס
"טעות מיליארד הדולר שלי", כך מתאר מי שהמציא את האפשרות שמשתנה יהיה שווה לnull
את ההמצאה שלו. ולמה? עקב האחריות של אפשרות זו להרבה מאוד שגיאות קוד... אבל אי אפשר באמת לכתוב קוד בלי null
. ולכן על מנת להתמודד עם הבעיות ששימוש בו עלול ליצור נסקור את הקונספט שקרוי בטיחות נאל (null safety
) שמה שעומד מאחוריו הוא בעצם תכנות בצורה אחראית, שתמנע את אותן שגיאות הnull
.
שגיאות נאל
שגיאות null
בדרך כלל מסתכמות בבניסיון לגשת או לבצע פעולה על אובייקט כלשהו, שבכלל לא קיים (כלומר, הוא null
). ולכן, בטיחות הנאל היא בגדול ווידוא שאותו האובייקט באמת קיים, לפני הגישה אליו. שפות מ"הדור החדש" כגון קוטלין ודארט בנויות בצורה המחייבת בטיחות נאל, ובהן כתיבה של קוד הניגש לערך שיכול להיות null
ללא בדיקה שהוא אינו כזה, תיתן שגיאה שלא תתן להריץ את הקוד כלל. בסי שארפ קיימים כל הכלים לבניית קוד בטוח נאלית, אך בניגוד לשפות מודרניות היא לא כופה בטיחות נאל, ואם נרשום קוד כזה יופיעו לנו אזהרות בלבד. נתייחס לאזהרות אלו כחובת יישום במהלך מדריך זה, למרות שבעקרון ניתן להתעלם מהם.
כברירת מחדל אזהרות בטיחות הנאל והכלים לבנייתה מאופשרים בפרוייטקים בסי שארפ, במידה ואינם, נדרש לשנות בקובץ xml
הפתרון את הערך Nullable
לenable
. או שנרשום מעל הקוד הבטוח נאלית את התג, #nullable enable
.
כתיבת הקוד
משתנים
על מנת להגדיר משתנה הלא יכול לקבל ערכי null
, נגדיר משתנה כמו שידענו להגדיר עד עכשיו, לדוגמא משתנה מהסוג string
,
#nullable enable
string MyName = "Tomer Ashtamker";
string? MyNullName = null;
אל המשתנה הראשון שנגדיר, MyName
לא נוכל להכניס ערכים שהינם נאל, ועל מנת ליצור משתנה שניתן להכניס אליו את ערך זה, נוסיף ?
לאחר סוג המשתנה. כמו במשתנה השני שהגדרנו, MyNullName
.
עבודה עם משתנים מאופשרי נאל
דוגמא א'
לא תמיד נוכל להתחמק מגישה למשתנים העלולים להכיל את הערך null
, ועבור מקרים אלו ישנם כמה קיצורי דרך האפקטיביים הרבה יותר מכתיבת פעולות if
עם מליון סעיפים.
#nullable enable
class Person{
____public string FirstName {get;set;} = "None";
____public string? LastName {get;set;}
____public Phone? PersonalPhone {get;set;}
____public Person (string FirstName,string? LastName, Phone? PersonalPhone)
____{
________this.FirstName = FirstName;
________this.LastName = LastName;
________this.PersonalPhone = PersonalPhone;
____}
}
class Phone
{
____public string? Number {get;set;}
____public Phone(string? Number)
____{
________this.Number = Number;
____}
}
Person?[]? Persons = new Person[3];
Persons[0] = new Person("Tomer","Ashtamker",new Phone(null));
Persons[1] = new Person("Ana", null, new Phone("0525381648"));
Persons[2] = new Person("Kanye","East",null);
for(int i=0;i<3;i++)
____Console.WriteLine(Persons?[i]?.PersonalPhone?.Number ?? "No Phone Assigned");
מה בעצם יש פה? אז ראשית הגדרתי שני קלאסים:
- איש - מכיל את המאפיין
FirstName
הלא יכול להיות נאל, מאותחל כ"None"
. בנוסף המאפייניםLastName,PersonalPhone
היכולים להיות נאל, הראשון הואstring
והשני עצם מסוג טלפון. - טלפון - מכיל את המאפיין
Number
שהינו סטרינג האמור להכיל את מספר הטלפון, ויכול להיותnull
.
לאחר מכן יצרתי את המערך Persons
המכיל שלושה אובייקטים מהסוג Person
. המערך עצמו מוגדר כך שהוא יכול להיות נאל, וכך גם העצמים שבתוכו (שימו לב לסימן ה?
). ארצה להדפיס את מספר הטלפון של כל אחד מהאנשים במידה וקיים, ואציין זאת במידה ולא, וכל זאת בצורה בטוחה נאלית. כיצד אעשה זאת בפשטות? בעזרת הפעולות,
?[]
- גישה למקום כלשהו במערך. במידה והמערך שווה לnull
, פעולת הגישה, והפעולות שאחריה לא יתקיימו (וכך לא תיווצר שגיאה), ויוחזר הערךnull
.?.
- גישה למאפיין פנימי של אובייקט. במידה והאובייקט הואnull
, פעולת הגישה, והפעולות שאחריה לא יתקיימו, ויוחזר הערךnull
.??
- במידה והאובייקט מצד שמאל לסימן הואnull
יוחזר האובייקט מצד ימין. במידה ולא השמאלי יוחזר.
בעצם בעזרת הפעולות האלו בניתי בשורת קוד אחת את הפעולה שתכננתי (השורה האחרונה) הפועלת כך,
- ניגש למקום כלשהו במערך
Persons
, במידה והמערך הוא נאל, יוחזרnull
(עם?[]
). - ניגש לעצם
Person
שנלקח מהמערךPersons
, במידה והעצם הוא נאל, יוחזרnull
(עם?.
). - ניגש למאפיין
PersonalPhone
של העצםPerson
, במידה והמאפיין הוא נאל, הערך נאל יוחזר (עם?.
). - ניגש למאפיין
Number
של העצםPhone
. - במידה ובכל אחד מהצעדים הקודמים הוחזר
null
, יוחזר הסטרינג"No Phone Assigned"
במקום (עם??
).
למשל, עבור האובייקטים שהזנו, יודפס ללא שגיאות,
"No Phone Assigned"
"0525381648"
"No Phone Assigned"
דוגמא ב'
עוד שתי פעולות שנוכל להשתמש בהם הם,
??=
- במידה והאובייקט מצד שמאל הואnull
, יוזן אליו הערך מצד ימין. במידה ולא לא יקרה כלום.!.
- גישה למשתנה העלול להיותnull
בעזרת עקיפת אזהרת בטיחות הנאל. נשתמש מתי שנדע מראש שהמשתנה שמאפשר אחסון ערך נאל בו, לא מאחסן כזה. במידה ומאחסן נקבל שגיאת נאל.![]
- כמו האחרון, רק לגישה למקום כלשהו במערך.
לדוגמא,
#nullable enable
void FillPersonsArray (Person?[]? PersonsArr)
{
____for(int i=0;i<(PersonsArr?.Length ?? 0);i++)
____{
________PersonsArr![i] ??= new Person("Israel","Israeli",new Phone("1234567890"));
____}
}
Person?[]? Persons = new Person[5];
FillPersonsArray(Persons);
ניצור פעולה המקבלת מערך של אנשים, היכול להיות נאל, והעצמים בתוכו יכולים להיות נאל. תפקידה למלא את המערך במקומות בהם המערך מכיל ערך null
, בעצם Person
חדש עם נתונים גנריים. על מנת לעשות זאת נבצע את הפעולות הבאות,
- ניצור לולאת פור, שתמשיך עד שתעבור על כל ערכי המערך. במידה והמערך הוא בכלל נאל, הלולאה לא תרוץ כי בעזרת הפעולה
??
יוגדר סיום הלולאה כשi<0
מה שלא יתקיים גם בהרצה הראשונה. - ניגש לאלמנט במקום
i
, בעזרת הפעולה![]
מכיוון שעקב תנאי הלולאה נדע שהמערך אינוnull
, שכן נכנסנו ללולאה. ולכן שימוש בפעולה יהיה בטוח. לאחר מכן נשתמש בפעולה??=
על מנת להזין למערך את עצם האיש הגנרי במידה והמיקום הנוכחי מכיל ערך נאל.
וכך, בעזרת שימוש בבטיחות נאל, נוכל לבנות יישומים הרבה יותר יציבים ומסודרים.
אין תגובות:
הוסף רשומת תגובה