|
|
|
Generics לעומק – חלק א'
|
המאמר "Generics לעומק" נכתב במקור על ידי ג' אמברוס ליטל (2004) ותורגם ועובד על ידי אייל סקיבא, ראש צוות פיתוח מערכות אינטרנט ארגוניות בחברת Netwise
קישור למאמר המקורי: http://www.15seconds.com/issue/040525.htm
לקריאת חלק ב' של המאמר: Generics לעומק – חלק ב'
|
|
|
תקציר המאמר:
מפתחים רבים מתקשים להבין את היכולת החדשה שנקראת Generics, שהתווספה בגרסה החדשה של NET 2., למרות שמדובר ביכולת שימושית מאוד. מאמר זה ינסה להסביר את הרתיעה מיכולת חדשה זו וכן ייתן הסברים המדגישים את קלות השימוש ביכולת וכמה שימושית היא יכולה להיות בסיטואציות פיתוח רבות. בעזרת ההסברים והדוגמאות במאמר מקווה המחבר, כי המפתחים יקבלו ביטחון ויחלו ליצור מבנים כלליים (Generic Types) ומתודות כלליות (Generic Methods).
|
|
|
|
אחד הדברים הבולטים בגרסה החדשה של NET. היא יכולת שנקראת Generics. למרות שהיכולת החדשה של ה-Generics שימושית מאוד, נראה שקשה לרבים לקבל אותה. ייתכן שהסיבות לכך נעוצות בעובדה ש-VB מעולם לא תמכה באפשרות זו ואילו ++C תומכת (באמצעות תבניות), וכן בגלל שצוות הפיתוח שפיתח את #C ודחף את התוספת של מאפיין זה, הוביל לתפיסה שמדובר בתכונה מתקדמת ורק אנשי פיתוח ברמה גבוהה יכולים להבין את הנושא.
|
|
|
|
במאמר זה מקווה המחבר לפזר, ככל הניתן, את ההילה מתעלומה זו על ידי הסבר מעמיק של התמיכה ב-Generics ב-NET. וכן להראות כמה קל להשתמש ביכולת זו בסיטואציות פיתוח רבות. המחבר גם מקווה כי כתוצאה מכך יבינו הקוראים את הרעיון מאחורי ה- Generics וכיצד ניתן להשתמש בו ואולי אף יקבלו ביטחון וייצרו מבנים (Types) ומתודות, המשתמשים ב-generics.
|
|
|
|
מדוע Generics הוא כלי מועיל לפיתוח?
|
|
|
|
לפני שנמשיך, אנו צריכים להבין שמבנה (Type) ב-NET. הינו שם פשוט וקצר עבור "מבנה של נתונים" או "מבנה נתונים". המבנה מציין למחשב כיצד לאחסן מידע (נתונים) ומה ניתן לבצע עם מידע זה.
|
|
|
|
ישנן דרכים רבות לאחסן נתונים ודרכים שונות שנתונים יכולים להשפיע על נתונים אחרים. לכן עלינו להנחות את המחשב שהמידע צריך להיות מאוחסן "בדרך זו" וכי צריך להשתמש במידע "בצורה כזו". וכאן, בעצם, נכנסים לתמונה מבני הנתונים.
|
|
|
יש מבנים מסוגים שונים ב-NET. כגון: מחלקות (enums ,(classes, נציגים (delegates), מבנים (structs) ואירועים (events). מחלקות הן המבנה השכיח ביותר ורבים מאתנו מזניחים בדרך כלל את המבנים האחרים. אנחנו אפילו אומרים "class library" כאשר למעשה במקרים רבים, אם לא ברוב המקרים, אותו "class library" מכיל סוגים שונים של מבנים.
|
|
|
|
אז כאשר אנחנו אומרים "Type" אנחנו בעצם מגדירים כיצד לייצג את המידע ואילו פעולות ניתן לבצע על אותו מידע. וכשאנו אומרים "strongly typed", אנחנו בעצם מתכוונים למבנה בעל מספיק אינפורמציה להתמודד עם נתונים מסוג מסוים, כך שסביבת הפיתוח תוכל להתריע לך מראש (לדוגמא, בתכנון ו/או בזמן קומפילציה) אם אתם משתמשים ב-Type כהלכה ("type checking"), אפילו במקרה שVisual Studio עוזר לכם ומציג את התכונות האפשריות של ה-type (לדוגמא, Intellisense).
|
|
|
|
בנוסף, ניתן לקבל עזרה מסביבת הפיתוח, המציעה אפשרויות לביצוע אופטימיזציה בצורה הטובה ביותר להתמודדות עם הנתונים, וכתוצאה אפשר להגיע לקוד איכותי יותר ולשפר ביצועים ומהימנות. שני רעיונות אלו הנם תנאים מוקדמים להבין מדוע Generics הינו כלי מועיל לפיתוח.
|
|
|
|
בעיות שנפתרו באמצעות שימוש ב- Generic
|
|
|
|
עכשיו, שברור לנו המינוח הבסיסי, אנו יכולים להתקדם לנושא העיקרי ולענות על השאלות: מה עושה generics? מדוע הוא חשוב? מה הבעיות שהוא בא לפתור? איך הוא עושה את החיים יותר קלים? ובסופו של דבר, מדוע צריך אותו בכלל?
|
|
|
|
מפתחים ב-NET. יכולים להפיק תועלת מהתכונות החדשות של generics על ידי שימוש במבנים הפנימיים של System.Collections.Generic או באמצעות ניצול היתרון של הטכנולוגיה, על מנת לכתוב מבנים יותר טובים בעצמנו. הסיבות לטענות אלו יתבהרו בהמשך במאמר זה.
|
|
|
|
התועלת הגדולה ביותר וכנראה הכי נפוצה למפתחים, היא האפשרות ליצור אוספים (collections) שמכילים מבנים. האוסף הבסיסי ביותר הינו מערך. עבודה עם מערכים יכולה להיות קשה, במיוחד כשמדובר על אחסון סוגים שונים של פריטים במערך וכשצריך לשנות את סדרם. כדי לפתור מגבלות אלו ישנם אוספים שמתנהגים בצורה ידידותית יותר ממערכים, שמאפשרים לנו להשתמש באוספים של מבנים בצורה יותר אינטואיטיבית, כפי שאנחנו עובדים עם אוסף של פריטים בעולם האמיתי.
|
|
|
|
הבעיה עם אוספים בגרסה הראשונה של NET. הייתה שעל מנת ליצור מבנה strongly typed המפתח היה צריך לכתוב אוספים עבור כל סוג של מבנה, ולדרוס או ליישם שגרות (כגון הוספה, הסרה) ו-indexers על כל אחד מהמבנים, שבאופן עקרוני היו רק שיכפול של קוד עבור כל מבנה. צורת עבודה זו גרמה לחיים קשים עבור המפתחים, שלא לדבר על כמות הקוד המיותר.
|
|
|
|
גם העיבוד של אוסף מבנים היה קשה, כי כדי ליצור מבנים היה עליו להשתמש במחלקת הבסיס-אובייקט על מנת לספק את הגמישות הנחוצה. סוגי נתונים כגון: Int16, Int32, בוליאני, ואחרים גרמו למחשב כאב ראש לא קטן כיוון שהיה צריך לארוז אותם לתוך חבילה מיוחדת הידועה כ-box (הביצוע נקרא "boxing"), בכדי לאחסן אותם באוסף (האוסף שנוצר היה אוסף של אובייקטים, שהפנה למבנה). למעשה, זו הייתה השיטה היחידה ליצור משהו גמיש לשימוש. כאשר המחשב עובד קשה, יש פגיעה בביצועים וכאשר יש פגיעה בביצועים, אז האצבע המאשימה מופנית אלינו המפתחים.
|
|
|
|
מבנים כלליים ושגרות כלליות
|
|
|
|
כעת נעסוק במבנים כלליים (generic types) ושגרות כלליות (generic methods). מבנים כלליים מאפשרים למפתחים לקבל אוספים של מבנים חזקים (strongly-typed), ללא צורך לשכתב את הקוד עבור כל אוסף שוב ושוב (לדוגמא, הוסף, הסר, הכנס, וכו. שגרות ) וללא ניפוח של הקוד עם מימושים שונים של מבנים מסוגים שונים, האמורים לעבוד באותה צורה.
|
|
|
|
מבנים כלליים שימושיים מאוד, כך שהרבה תרחישים יטופלו על ידי צורה זו של מבנה באמצעות שימוש בספרייה System.Collections.Generic (אלא אם המבנה צריך לממש תפקודיות מיוחדת ושונה) ואין צורך אפילו בהגדרה של אוסף יחיד בקוד.
|
|
|
|
את אותן "הטבות" ניתן להרחיב לכל מצב שבו המפתח רוצה לתת לצרכנים (מפתחים אחרים) לפרט בעצמם באיזה סוג אמור להיות המבנה. זאת אומרת, ש-generics מאפשר לנו לקבל מבנה עם משתנים כלליים שניתן להגדיר את הסוג שלהם בקוד, ואנו יכולים לכתוב שיגרה אחת (או יותר) המותאמת לפרמטר. לדוגמה, להחזיר מהשגרה את סוג הנתונים שהצרכן ביקש, לקחת קלט של פרמטר לפי הסוג שהצרכן ציין או לחשוף תכונה לפי הסוג שהצרכן ציין.
|
|
|
Generics לא רק עושה לנו את החיים יותר קלים, הוא גם מאפשר למפתחים לבצע את מה שהם לא היו יכולים לבצע ללא השיטה החדשה של כתיבת קוד. דוגמה טובה לשימוש ביכולת החדשה היא במקרים של ריבוי צורות (polymorphism). לדוגמה, אם המפתח רוצה לכתוב מחלקת בסיס בעלת שיגרה שמחזירה מופע או אוסף של טיפוס נגזר, באפשרותו ליצור שיגרה כללית שמחזירה סוג מבנה שצוין על ידי הצרכן, מה שאומר שכל מבנה ייגזר מהמבנה הבסיסי. בנוסף להקלה זו למפתחים, generics יכול גם למנוע צורך משימוש במנגנונים שמבצעים boxing לסוגי המידע הפשוטים ובכך למנוע ביצוע הפשטה של אובייקטים.
|
|
|
|
כללי מינוח ותחביר
|
|
|
|
המונח הראשון שאנו צריכים להכיר הוא "type parameter". מבנה פרמטר הוא הבסיס לפיתוח פונקציונליות כללית. זהו פרמטר הזהה לכל פרמטר אחר שמעבירים בעת שמשתמשים במבנה או שיגרה. עבור מבנים, זהו פרמטר כמו של ה-constructor. עבור שגרות, type parameter הוא כמו פרמטרים נוספים חוץ מהעובדה שהוא בדרך כלל מוסק מהצורה בה נכתב הקוד. רישום 1 מציג את התחביר עבור הגדרה של מבנה פרמטריאלי במבנים ובשגרות.
|
|
|
רישום 1 - Type Parameters
|
|
|
|
זה מוביל אותנו למכלול השני של מונחים שאנו אמורים להכיר: "generic type"
ו-"generic method". מבנה כללי הינו מבנה (type, struct, delegate, או event) שמגדיר אחד או יותר סוגי פרמטרים למבנה, כפי שמופיע ב-MySample ברישום 1. שיגרה כללית היא כל שיגרה שמגדירה פרמטר כללי אחד או יותר, כפי שמופיע בשיגרה DoSomething ברישום 1. חשוב לשים לב שניתן להגדיר שגרות כלליות גם בתוך מבנים שאינם מבנים כלליים. אלו היו שלושת המונחים הבסיסיים - type parameter, generic type ו generic method – ואיתם אנו בעצם נעבוד בצורה שוטפת במימוש generics.
|
|
|
|
מכלול המונחים הבא יכסה את השימוש במבנים כלליים ושגרות כלליות. ראשית, המונח "constructed type" אומר שהמבנה נוצר (constructed) כתוצאה משימוש במבנה כללי. לדוגמה, אילו היינו אמורים להשתמש במבנה הכללי MySample מרישום 1, היינו מכריזים על המבנה כפי שמודגם ברישום 2.
|
|
|
רישום 2 - Constructed Type
|
|
|
|
שימו לב שאנו מעבירים פרמטר ("type argument") למבנה MySample, המציין את סוגו של הפרמטר הכללי. התוצאה של הצהרה זו תהיה יצירה של בנאי (constructor) למבנה בזמן ריצה.
בנקודה זאת, יש להסביר את ההבדלים בין הבנאים השונים של מבנים: "closed constructed types" ו-"open constructed types". ברישום 2 ישנה דוגמה לבנאי סגור. מה שעושה את זה "סגור" זאת העובדה שבזמן כתיבת הקוד אנו יודעים מה סוג הפרמטר שעובר לבנאי של המבנה - System.String.
|
|
|
|
בכל אופן, אם נבחן את הדוגמה ברישום 3, נראה שסוג המשתנה שאנו מגדירים במבנה MySample הוא מסוג הפרמטר שמועבר למבנה עצמו. לכן, בזמן כתיבת הקוד אנו לא יודעים איזה סוג של פרמטר יגיע למבנה בזמן הריצה. צורה זו של כתיבה נקראת "בנאי מבנה פתוח", מפני שסוג המשתנה פתוח לכל הסוגים שמועברים אליו.
|
|
|
רישום 3 - Open Constructed Type
|
|
|
|
לסיכום,
מאמר זה הסביר את היכולת החדשה - generics ואת הקווים המנחים לפיתוח באמצעות כלי זה, נתן הסברים ודוגמאות המדגישים את קלות השימוש וכן לשימושיות הרבה היכולה להופיע בסיטואציות פיתוח רבות. בגיליון הבא נסקור אפשרויות נוספות שנותן לנו המנגנון בהרחבת והגבלת סוגי הפרמטרים של מבנים כלליים, ונשווה את המנגנון למנגנונים דומים בשפות אחרות.
|
|
|
|
|