
הטקסט המלא של הדקות הראשונות בהרצאה «Interoperability - חלק 2» בקורס הווידאו של PracticU – כתוביות בעברית עם זמן התחלה לכל פסקה. כך תוכלו לקרוא ולחפש את הנושאים לפני שמתחילים ללמוד.
הדבר האחרון שאנחנו הולכים לראות זה איך אנחנו עובדים עם זיכרון Unmanaged Code בדוטנט. בואו נפתח פרויקט חדש New Project, נגדיר פה Work with Unmanaged Code.
אוקיי, האמת שזה לא Unmanaged Code, זה Unmanaged Memory. בואו נצרף את הפרויקט הזה. ומה הכוונה שאנחנו רוצים לעבוד עם זיכרון שהוא Unmanaged?
זיכרון שהוא Unmanaged זה בעצם זיכרון של שפת C או C++, שם אין שום garbage collector ולכן הייתם יכולים פשוט להגיד למערכת ההפעלה תביא לי בבקשה מערך של 1000 בתים, מערכת ההפעלה הייתה מביאה לכם מערך של 1000 בתים מהזיכרון שלה, לא היה שום garbage collector שמנהל את זה והייתם צריכים לבצע איזושהי פונקציה של
delete, קרוא לאופרטור delete או לפונקציית free אם הייתם בשפת C בשביל להחזיר למערכת ההפעלה את הזיכרון הזה. אבל אנחנו לא יכולים לעבוד סתם עם זיכרון שהוא unmanaged, אלא אנחנו צריכים לעבוד עם רכיבי .NET.
בוא נניח שקיים איזשהו רכיב ב-C++ או ב-C, איזשהו DLL כזה, שמביא לי מערך, הוא יביא לי את המערך, הוא יחזיר לי את המערך על ידי איזושהי פונקציה, ואני רוצה עכשיו לקבל אותו לאיזשהו מערך.
זאת אומרת שהולך להיות לנו איזשהו מערך שמקבל את אותו זיכרון שקיים באותו DLL. אבל הזיכרון הזה זה זיכרון שהוא ספציפית קיים איפשהו במחשב.
מה בדיוק הבעיה שכרגע תהיה לנו? הבעיה שתהיה לנו היא שה garbage collector יכול לקחת את המערך שלנו ולגרום לו להצביע פתאום על משהו אחר. כרגע אני אעשה הדמיה כאילו שאני מקבל איזשהו מערך אמיתי מאיזשהו dll בשפת c.
אם אתם תיתקלו בזה במציאות אז אתם פשוט רואים איזושהי פונקציה בשפת c שמחזירה לכם מערך ואתם מקבלים אותו ומשתמשים איתו. אז אני כרגע אעשה מערך שמכיל כמה ערכים כאילו שקיבלתי אותם.
בואו נלך פה על כמה מספרים כאלה. ויש לנו פה אחלה של מערך. אז למרות שהמספרים הם הוגדרו בדוטנט, זאת בעצם הדמיה כאילו שהמערך הזה הגיע מ-C או C++.
אם זה המצב, בשביל לעבוד עם המערך הזה ולרוץ עליו, אנחנו צריכים לעשות כמה דברים. קודם כל, אנחנו צריכים להגדיר שאנחנו נמצאים באזור שנקרא Unsafe.
למה האזור הזה נקרא Unsafe? מפני שיש פה איזשהו קוד שהוא קיים בזיכרון שהוא לא מנוהל על ידי ה garbage collector, unmanaged code לחלוטין. ולכן האזור הזה נקרא unsafe code.
אנחנו רואים שיש לנו פה איזושהי שגיאה מפני שby default .NET לא מאשרת לעבוד עם unsafe code. בשביל לטפל בבעיה הזאת ולהגיד ל .NET הפרויקט הספציפי הזה אני כן רוצה באזור הזה לעבוד עם unsafe code, אנחנו צריכים ללכת לפרופרטיז של הפרויקט, אנחנו צריכים ללכת לכרטיסייה שנקראת build ולסמן את הדגל allow unsafe code. בוא תאשר לי לעבוד עם קוד לא מנוהל של C וC++. בואו
נשמור, נבצע rebuild, אנחנו רואים שהשגיאה נפתרה. הדבר השני שאנחנו צריכים לעשות זה פקודה שנקראת fixed.
כל המילים האלה זה מילים שמורות של .NET שמאפשרות לעבוד עם Unsafe Code או Unmanaged Memory. פקודת Fixed אומרת לנו, אתם יכולים עכשיו להציב פוינטר.
הנה עבודה עם פוינטרים כמו שעבדנו פעם לפני 100 שנה ב-C++ ובשפת C. פוינטר זה סך הכל מצביע לאזור זיכרון. הדבר המקביל אליו זה רפרנס, אבל יש איזשהו הבדל בין פוינטר לבין רפרנס.
רפרנס זה סך הכל משתנה שמצביע על אזור זיכרון, ואנחנו משתמשים איתו בשביל להגיע לאזור זיכרון בצורה ישירה.
פוינטר מצביע על האזור זיכרון, ובשביל להיכנס ולהשתמש באזור זיכרון אנחנו כאילו צריכים לרדת רמה מהפוינטר לאזור זיכרון, תכף נראה מה זה אומר.
אז הנה אנחנו משתמשים באינט כוכבית, כוכבית זה הסימן של פוינטרים, נפתח פה ptr, ונכניס לתוכו את ARR. למה אנחנו צריכים להשתמש עם פקודת פיקסט?
מה שאנחנו בייסיקלי אומרים בפקודת פיקסט זה אנחנו אומרים לגארבג' קולקטור אל תזיז את המערך הזה גם אם אתה תיכנס לפעולה. אל תיקח אותו מג'נריישן 0 ותעביר אותו לג'נריישן 1 או מג'נריישן 1 ותעביר אותו לג'נריישן 2 כי אנחנו שמים עליו איזשהו פוינטר ואסור להזיז אותו.
כאן אנחנו רואים את הפוינטר עצמו שמצביע על ההתחלה של המערך, ואנחנו יכולים עכשיו להשתמש בפוינטר בשביל לרוץ על כל המערך. בואו נניח שיש לנו כאן איזשהו int size שווה arr length, ניקח את הגודל של המערך עצמו, ופה אנחנו יכולים לרוץ, נרוץ עד size, פה אנחנו רוצים בייסיקלי
להשתמש בפוינטר ולהדפיס את המידע. אז יש לנו שתי אפשרויות. אפשרות ראשונה זה להתייחס לפוינטר הזה כאילו שהוא מערך ולהציג ptr במיקום ולגשת ל-i.
זאת אפשרות ראשונה, בואו נריץ. אנחנו רואים את כל המספרים מוצגים. אפשרות שנייה זה לארגן עוד פוינטר ptr to print, להכניס לו את הפוינטר הראשון והפעם אם אנחנו רוצים לעשות את זה בואו נשרשר אותם אנחנו צריכים לעשות את זה על ידי כוכבית ptr2print זאת הסיבה או זה ההבדל בין פוינטר לבין רפרנס
אם יש לנו פוינטר שמצביע לזיכרון ב-c וב-c++ היינו צריכים בשביל להגיע למידע שהוא מצביע עליו לרדת עם כוכבית לתוך המידע זה נקרא לרדת רמה לתוך המידע כאשר רפרנס לא היינו עושים את זה אף פעם.
אז פוינטר ורפרנס זה ממש אותו הדבר מבחינת הקונספט שניהם מצביעים לאזור זיכרון אחר פוינטר רק בשביל להגיע לאזור זיכרון אנחנו צריכים להכניס לרדת רמה לרדת רמה זה או עם כוכבית או עם מיקום I. בנוסף אנחנו צריכים להזיז את הפוינטר זה יהיה ptr to print++ כאן אנחנו בעצם אומרים לו תזוז
למקום הבא שלך בואו נריץ ואנחנו רואים שיש לנו שתי הדפסות אותו הדבר שימו לב שאם אנחנו עושים פה חריגה אין שום garbage collector ששומר עלינו אין שום מנגנון או clr ששומר עלינו אם אנחנו חורגים מהזיכרון הכוונה אם אנחנו באים לפוינטר ואומרים לו אחלה שאתה מצביע לי על האזור זיכרון הזה ואני רוצה עכשיו לגרום לך להצביע על 100 בתים או 100 מקומות אחרים יותר בזיכרון.
מה שיקרה ב-unsafe code זה שהמערכת תאשר לעשות את זה, ולא יהיה פה בעצם איזושהי הגנה כמו שקיימת בדוטנט אם אתם חורגים מגבולות מערך, אז כמובן המערך עצמו אומר לכם אתם לא יכולים בבקשה תודה רבה ואני מעלה לכם איזושהי אקספשן.
המערכת יכולה לתת לנו לעשות את זה וזה יהיה מאוד מסוכן כי אנחנו לא אמורים לדעת מה יש בקפיצה של עוד 100 בתים או 100 מקומות בזיכרון אחרי הפוינטר הזה.
יכול להיות שיש שם איזה תוכנית או איזה קוד או אפילו חלק מהתוכנית שלנו שנמצא בזיכרון ואנחנו בעצם הורסים שם את הזיכרון. זאת הסיבה שכל הסיפור הזה נקרא אנסייף והוא יחסית נחשב די ריסקי וצריך לעבוד פה מאוד מאוד בעדינות אני כמובן לא אעשה את זה כרגע עכשיו אנחנו רואים שהכל עובד פרפקט, הצבנו פוינטר בזיכרון, רצנו עם הפוינטר הזה על מערך בשתי שיטות,
הצגנו את המידע. במידה וכל הפונקציה שלנו עובדת עם בלוק שהוא אונסייף, אז אנחנו יכולים להגדיר את כל הפונקציה כאונסייף.
בואו נעשה בעצם שתי דוגמאות לסיפור הזה, ניקח כאן static void unsafe function 1 את כל הקוד הזה נזרוק בתוך unsafe function 1 ונשכפל את כל הסיפור הזה ל-unsafe function 2 כאשר לא נגדיר פה קוד