בית פורומים למתכנתים שבינינו

מדריך C#

שלום אורח. באפשרותך להתחבר או להירשם
הצג 15 הודעות בעמוד הוסף לדף האישי  דווח למנהל שלח לחבר
נשלח ב-27/7/2011 11:25 לינק ישיר 
מדריך C# – העברת פרמטרים לפונקציה

בחלק הקודם למדנו כיצד להגדיר פונקציות בסיסית. בחלק זה נלמד כיצד להעביר לפונקציות פרמטרים ולקבל מהפונקציה את ערך החזרה.

מה הם פרמטרים של פונקציה?

פרמטרים הם ערכים שפונקציה יכולה לקבל מהקוד שקורא לה. השתמשנו כבר בפרמטרים של פונקציות כאשר קראנו לפונקציה Console.WriteLine והעברנו לה את התוכן להדפסה. תוכן זה הוא הערך שהעברנו כפרמטר לפונקציה.

הגדרת פרמטרים לפונקציה

כדי להגדיר שיש לפונקציה פרמטרים נרשום לאחר שם הפונקציה, בתוך הסוגרים את רשימת הפרמטרים, מופרדת בפסיקים. 
כל פרמטר מוגדר ע"י סוג הפרמטר ושמו.

לדוגמא, הפונקציה הבאה מגדירה שתי פרמטרים מטיפוס string בעלי השמות firstName ו lastName, הפונקציה משתמשת בפרמטרים בשביל להדפיסם למסך:

static void PrintFullName(string firstName, string lastName)
{
    Console.WriteLine("Full name: " + firstName + " " + lastName);
}


העברת ערכים בקריאה לפונקציה

העברת ערכים בקריאה לפונקציה נעשית ע"י כתיבת שם הפונקציה ובסוגריים את הערכים שאנו רוצים, מופרדים בפסיקים.

לדוגמא, כדי לקרוא לפונקציה שהגדרנו בסעיף הקודם נבצע:

static void Main(string[] args)
{
    PrintFullName("Arik", "Poznanski");
}

בדוגמא זו הערך "Arik" מועבר לפונקציה בתור הפרמטר firstName ואילו הערך "Poznanski" מועבר לפונקציה בתור הפרמטר lastName.


החזרת ערך מפונקציה

פונקציות יכולות להחזיר ערכים למי שקרא להם. כדי להחזיר פרמטר מפונקציה ראשית יש להגדיר בפונקציה את סוג הערך המוחזר, זה נעשה ע"י כתיבת טיפוס הערך המוחזר לפני שם הפונקציה.

לדוגמא, קטע הקוד הבא מגדיר פונקציה שמקבלת שני פרמטרים מטיפוס int ומחזירה ערך מסוג int:

static int Add(int x, int y)

ה- int שמופיע לפני שם הפונקציה Add מציין את טיפוס ערך ההחזרה של הפונקציה.

כדי לבצע בפועל את החזרת הערך נשתמש בפקודה return בתוך גוף הפונקציה.

לדוגמא, הפונקציה הבאה מקבלת שני פרמטרים ומחזירה את סכומם:

static int Add(int x, int y)
{
    return x + y;
}

שימו לב שהפקודה return היא הפקודה האחרונה שמתבצעת בפונקציה. פקודה זו מבצעת החזרה של הערך המבוקש וסיום הפונקציה.

כדי לקבל את הערך בפונקציית הMain נצטרך להציב את תוצאת הפונקציה לתוך משתנה באופן הבא:

static void Main(string[] args)
{
    int z = Add(5, 3);
}




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:25 לינק ישיר 
מדריך C# – פרמטרי ref ופרמטרי out

בחלק הקודם למדנו כיצד להעביר פרמטרים לפונקציה וכיצד לקבל מפונקציה ערך החזרה. 
בחלק זה נלמד על שני סוגים נוספים של פרמטרים לפונקציה. 

הקדמה

כדי להבין את השימוש של פרמטרים מסוג ref נתבונן בדוגמא הבא:

static void swap(int x, int y)
{
    int z = x;
    x = y;
    y = z;
}

פונקציה זו מקבלת שני פרמטרים מטיפוס int בשם x ו y ומבצעת החלפה בערכים שלהם בעזרת משתנה עזר z. 
כעת נכתוב תוכנית שקוראת לפונקציה swap על שני משתנים בשם a ו b ומדפיסה את הערכים שלהם לפני ואחרי הקריאה:

static void Main(string[] args)
{
    int a = 5;
    int b = 7;
   
    Console.WriteLine("a = " + a.ToString());
    Console.WriteLine("b = " + b.ToString());
    swap(a, b);
    Console.WriteLine("a = " + a.ToString());
    Console.WriteLine("b = " + b.ToString());
}

תוצאת הרצת התוכנית הזו היא:

מדריך #C – פרמטרי ref ופרמטרי out

כלומר, למרות שהפעלנו את הפונקציה swap המשתנים לא הוחלפו!

אז מה קורה כאן?

הסיבה שלא ראינו שינוי בערכים של a ו b היא שכאשר מעבירים לפונקציה ערכים בשפת C#, הערכים מועתקיםלתוך הפרמטרים. 
אם נזכור שמאחורי כל משתנה יש איזשהו תא בזיכרון שמכיל את הערך שלו, אז במקרה שלנו יצרנו 2 תאים נוספיםשמכילים העתקים של התאים המקוריים.

הפונקציה swap בעצם עשתה החלפה של הערכים במשתנים החדשים, אבל לא המקוריים! המקוריים נשארו עם אותם ערכים ולכן מציגים אותה תוצאה לאחר הקריאה ל swap.

פרמטר מסוג ref

כדי לגרום לפונקציה swap לעבוד כמו שצריך, נרצה לסמן לפונקציה שהפרמטרים צריכים להשתמש באותם תאים בזיכרון ולא ליצור העתקים בתאים חדשים.

ניתן לבצע זאת ע"י סימון כל פרמטר במילה ref לפני הגדרת הטיפוס שלו.

בדוגמא שלנו ההגדרה צריכה להיות:

static void swap(ref int x, ref int y)
{
    int z = x;
    x = y;
    y = z;
}

כעת הפונקציה לא תבצע העתקה של הפרמטרים אלא תשתמש באותם תאים של המשתנים המקוריים.

נעיר שכאשר משתמשים בפרמטרים מסוג ref צריך לציין זאת גם בעת הקריאה לפונקציה swap, כלומר כעת התוכנית תראה כך:

static void Main(string[] args)
{
    int a = 5;
    int b = 7;
   
    Console.WriteLine("a = " + a.ToString());
    Console.WriteLine("b = " + b.ToString());
    swap(ref a, ref b);
    Console.WriteLine("a = " + a.ToString());
    Console.WriteLine("b = " + b.ToString());
}

והרצת התוכנית תיתן את הפלט:

מדריך #C – פרמטרי ref ופרמטרי out

כעת אנו רואים שהערכים של המשתנים a ו b אכן התחלפו כנדרש. 

פרמטר מסוג out

פרמטרים מסוג out באים לפתור בעיה שונה. 
ראינו בחלק הקודם במדריך כיצד להחזיר ערך מפונקציה ע"י הגדרת ערך החזרה ושימוש בפקודה return. 
נשאלת השאלה, מה נעשה אם נרצה שפונקציה תחזיר יותר מערך אחד?

לצורך כך נוכל להגדיר פרמטר של הפונקציה בתור פרמטר out, ע"י הוספת המילה out לפני הגדרת הטיפוס שלו.

פרמטר שמוגדר בתור out הוא פרמטר שמועבר ע"י הפונקציה הקוראת (Main) רק לצורך קבלת ערך החזרה. 
לדוגמא, קטע הקוד הבא מגדיר פונקציה שיש לה גם ערך החזרה וגם שני פרמטרים מטיפוס out:

static string GetMultipleValues(out string firstName, out string lastName)
{
    Console.Write("Enter first name: ");
    firstName = Console.ReadLine();

    Console.Write("enter last name: ");
    lastName = Console.ReadLine();

    return firstName + " " + lastName;
}

פונקציה זו קולטת מהמשתמש ערכים לתוך הפרמטרים firstName ו lastName ובנוסף מחזירה ערך שמכיל את השם המלא.

בעת הקריאה לפונקציה עם פרמטר מטיפוס out יש לציין את המילה out לפני שם המשתנה:

static void Main(string[] args)
{
    string full, first, last;
    full = GetMultipleValues(out first, out last);
}

בפונקציה זו אנו מגדירים ראשית 3 משתנים מטיפוס string. 
לאחר מכן אנו קוראים לפונקציה GetMultipleValues ומעבירים לה את הפרמטרים first ו last תוך הוספת המילהout לפני. 
המשתנה full יקבל את ערך ההחזרה הרגיל של הפונקציה ואילו המשתנים first ו last יקבלו את הערכים שיקלטו מהמשתמש.




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:25 לינק ישיר 
מדריך C# – מערכים (arrays)

בחלק זה נלמד על כיצד לעבוד עם מערכים בשפת #C. 

מה זה מערך?

מערך (array) הינו טיפוס נתונים המכיל מספר משתנים מאותו סוג שיושבים בזיכרון בצורה רציפה, כלומר אחד ליד השני. 
כאשר מגדירים משתנה מטיפוס מערך יש לציין את מספר המשתנים במערך, סוג המשתנים ושם המערך.

ניתן להתייחס למערך בצורה ויזואלית כאל שורה של משתנים רגילים:

מדריך #C למתחילים – מערכים

בדוגמא בציור יש לנו מערך של משתנים מטיפוס int בשם arr, המערך הוא בגודל 10. 
הפניה למערך מתבצעת באמצעות שם המערך והאינדקס לאיבר, שימו לב שהאינדקס בשפת #C מתחיל מ 0, כלומר [arr[0 הוא האיבר הראשון במערך, [arr[1 הוא האיבר השני וכו' 

כיצד יוצרים מערך?

כדי לראות כיצד ליצור מערך, נתבונן בדוגמא הבא:

int[] arr = new int[10];

בדוגמא זו יצרנו מערך של int בשם arr, בעל גודל 10. 
ראשית הגדרנו את סוג המשתנה []int, כלומר מערך של int, לאחר מכן נתנו לו שם arr ואז ביקשו להקצות זיכרון ל10 תאים ע"י שימוש בפקודה new.

נראה דוגמא נוספת:

string[] myStrings = new string[500];

בדוגמא זו יצרנו מערך של string בשם myStrings בעל גודל 500, כלומר נוכל לאכסן במערך שלנו 500 משתנים מטיפוס string.


כיצד משתמשים במערך?

לאחר שיצרנו את המערך נוכל להשתמש בו בשביל לכתוב ולקרוא ערכים. 
הגישה לאיברים במערך מעשית ע"י ציון שם המערך והאינדקס של האיבר הדרוש.

לדוגמא, הקוד הבא ניגש לתא הראשון במערך בשם arr ומציב שם את המספר 5:

arr[0] = 5;

הקוד הבא ניגש לתא השלישי במערך myStrings ומציב שם את הערך "Hello":

myStrings[2] = "Hello";

כיצד עוברים על איברי המערך?

כדי לעבור על איברי המערך נשתמש בדרך כלל בלולאה. 
לדוגמא, לולאת for הבאה עוברת על איברי המערך arr ומציבה בהם את הספרים 1 עד 10:

for (int i = 0; i < arr.Length; ++i)
{
    arr[i] = i + 1;
}

שימו לב לשימוש בתכונה Length של המערך arr, תכונה זו מחזירה את גודל המערך, במקרה שלנו arr.Length מחזירה את הערך 10.

נראה דוגמא נוספת שקולטת מספרים מהמשתמש ומציבה אותם במערך:

static void Main(string[] args) 
{
    int[] arr = new int[10];
    string tempNumber;
    for (int i = 0; i < arr.Length; ++i)
    {
        Console.Write("Enter number: ");
        tempNumber = Console.ReadLine();
        arr[i] = Convert.ToInt32(tempNumber);
    }
}

דוגמא מסכמת

נראה כעת דוגמא לפונקציה שמקבלת מערך של int ומחזירה את הממוצע שלהם (מסוג double):

static double Average(int[] array)
{
    double sum = 0;
    for (int i = 0; i < array.Length; ++i)
    {
        sum += array[i];
    }

    return sum / array.Length;
}




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:26 לינק ישיר 
מדריך C# - מערכים רב-מימדיים

בחלק הקודם במדריך למדנו כיצד לעבור עם מערכים. בחלק זה נרחיב את הידע שלנו למערכים רב-מימדיים.

מה זה מערך רב מימדי?

כדאי להתחיל ראשית בהסבר על מערך דו-מימדי. 
מערך דו-מימדי הוא מערך, כלומר אוסף איברים מאותו סוג שיושבים בזכרון בצורה רציפה, שכדי לגשת לאיברים שבו צריכים שני אינדקסים.

ניתן להתייחס למערך דו-מימדי בצורה ויזואלית כאל טבלה של משתנים רגילים:

מדריך #C למתחילים - מערכים רב-מימדיים

במערך זה יש שני מימדים, הראשון בגודל 3 והשני בגודל 10, ולכן סה"כ האיברים במערך הוא 30. 
בשביל לגשת לאיבר כלשהוא במערך צריך לתת את האינדקסים שלו בשני המימדים, לדוגמא arr[0,0] הוא האיבר בשורה הראשונה, בעמודה הראשונה.

באופן דומה ניתן להגדיר מערך תלת-מימדי, שבו צריך 3 אינדקסים כדי לגשת לכל איבר, וכן הלאה. 
לרוב נעבוד לכל היותר עם מערכים דו-מימדיים וכך גם יהיו הדוגמאות שלנו.

יצירת מערך דו-מימדי

יצירת מערך דו-מימדי נעשית באופן דומה ליצירת מערך רגיל:

int[,] arr = new int[3, 10];

בדוגמא זו יצרנו מערך דו-מימדי בעל מימד ראשון בגודל 3 ומימד שני בגודל 10, סה"כ מספר האיברים במערך הוא 30. 

גישה לאיברי המערך הדו-מימדי

בשביל לגשת למערך דו-מימדי יש לציין את שני האינדקסים של האיבר שאליו אנו מעוניינים לגשת. 
לדוגמא, קטע הקוד הבא ניגש לאיבר בשורה השלישית ובעמודה העשירית ומציב שם את המספר 7:

arr[2, 9] = 7;

מעבר על איברי מערך דו-מימדי

בשביל לעבור על כל האיברים במערך דו-מימדי נצטרך לעשות שימוש בלולאה מקוננת, כפי שלמדנו באחד החלקים הקודמים במדריך. 
הלולאה החיצונית תעבור על האינדקס הראשון ואילו הלולאה הפנימית תעבור על האינדקס השני:

int[,] arr = new int[3, 10];

for (int i = 0; i < arr.GetLength(0); ++i)
{
    for (int j = 0; j < arr.GetLength(1); ++j)
    {
        arr[i, j] = (i + 1) * (j + 1);
    }
}

בדוגמא זו אנו עושים שימוש בפונקציה arr.GetLength, פונקציה זו מקבלת את מספר המימד ומחזירה את גודלו. 
כלומר קריאה לפונקציה (arr.GetLength(0 תחזיר את גודל המימד הראשון, 3, ואילו קריאה לפונקציה(arr.GetLength(1תחזיר את גודל המימד השני, 10.

נציין ששימוש בתכונה arr.Length תחזיר את סך כל האיברים במערך, כלומר 30.




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:26 לינק ישיר 
מדריך C# - לולאת foreach

בחלקים קודמים של המדריך למדנו על סוגים שונים של לולאות בשפת #C וכן כיצד לעבוד עם מערכים. 
בחלק זה נלמד על סוג נוסף של לולאה שמותאם במיוחד לעבודה עם מערכים. 

לולאת foreach

לולאת foreach היא לולאה שמותאמת במיוחד לעבודה עם מערכים. בלולאה מסוג זה אין צורך להגדיר משתנה שימנה את מספר האיטרציות שעברנו. 
במקום זה הלולאה עוברת באופן אוטומטי על כל איברי המערך.

לדוגמא, בקטע הקוד הבא אנו יוצרים מערך של מחרוזות, קולטים לתוכו מחרוזות מהמשתמש בעזרת לולאת forרגילה ואז מדפיסים את המחרוזות שנקלטו באמצעות לולאת foreach:

// create array
string[] myStrings = new string[5];

// for loop
for (int i = 0; i < myStrings.Length; ++i)
{
    Console.Write("Enter string: ");
    myStrings[i] = Console.ReadLine();
}

// foreach loop
foreach (string currentString in myStrings)
{
    Console.WriteLine(currentString);
}

נשים לב למבנה הפשוט של לולאת foreach, אין משתנה מניה ואין גישה למערך בתוך גוף הלולאה. 
במקום זאת, המשתנה currentString מקבל ערך אחר בכל איטרציה של גוף הלולאה.

שימוש בלולאת foreach מאפשר לכתוב קוד שהוא יותר קריא ומסודר.




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:27 לינק ישיר 
מדריך C# - עבודה עם מחרוזות (string)

בחלק זה נלמד על פונקציות שימושיות נוספות כאשר עובדים עם משתנים מטיפוס string.

התכונה Length

לכל משתנה מטיפוס string יש תכונה בשם Length שמחזירה את אורך המחרוזת.

לדוגמא, קטע הקוד הבא מגדיר מחרוזת עם המילה "Hello World" ומדפיס את אורך המחרוזת, 11:

string myString = "Hello World";
Console.WriteLine(myString.Length);

גישה לאות בתוך המחרוזת

ניתן להתייחס למחרוזת כאילו היא הייתה מערך של תווים (char), כלומר נוכל לבקש מהמחרוזת להחזיר לנו אות שנמצאת בתא מסוים.

לדוגמא, קטע הקוד הבא מגדיר מחרוזת ומדפיס את האות הראשונה והאחרונה:

string myString = "Hello World";
char firstChar = myString[0];
char lastChar = myString[myString.Length - 1];
Console.WriteLine(firstChar); // prints H
Console.WriteLine(lastChar);  // prints d

שימו לב שכדי לגשת לתא האחרון במחרוזת משתמשים ב Length – 1, שכן מספור התאים מתחיל ב 0. 

הפונקציות ToLower ו ToUpper

זוג הפונקציות ToLower ו ToUpper מאפשרות לנו לבצע פעולה נפוצה במחרוזת שהיא שינוי מאותיות קטנות לאותיות גדולות.

לדוגמא, אם נפעיל את הפונקציה ToLower על המחרוזת "Hello World" נקבל "hello world", 
בנוסף, אם נפעיל את הפונקציה ToUpper על המחרוזת "Hello World" נקבל "HELLO WORLD". 
קטע הקוד הבא מדגים זאת:

string myString = "Hello World";
Console.WriteLine(myString.ToLower());
Console.WriteLine(myString.ToUpper());

להלן תוצאת הריצה של תוכנית זו:

C# מחרוזות


הפונקציות TrimStart, TrimEnd, Trim

הרבה פעמים קורה במחשבים שמחרוזת נקלטת עם רווחים עודפים מימין או משמאל. 
הפונקציה TrimStart מוחקת את כל הרווחים שיש בתחילת המילה, ואילו הפונקציה TrimEnd מוחקת את הרווחים שמופיעים בסוף המילה. 
הפונקציה Trim מבצעת את שתי הפעולות גם יחד.

לדוגמא, נריץ את קטע הקוד הבא על המחרוזת " Hello World ":

Console.BackgroundColor = ConsoleColor.Blue;
string myString = "   Hello World   ";
Console.WriteLine(myString.TrimStart());
Console.WriteLine(myString.TrimEnd());
Console.WriteLine(myString.Trim());

ונקבל את התוצאה:

C# מחרוזות

הערה: שיניתי את צע הרקע לכחול כדי שניתן יהיה לראות את הרווחים של המחרוזת. 

הפונקציות StartWith, EndWith

הפונקציה StartWith מאפשר לבדוק האם המחרוזת שלנו מתחילה במחרוזת אחרת. 
לדוגמא, הרצת הפונקציה StartWith על המחרוזת "Hello World" עם הפרמטר "Hello" תחזיר true מאחר והמחרוזת "Hello World" מתחילה במילה "Hello".

באופן דומה, הפונקציה EndWith בודקת האם המחרוזת שלנו מסתיימת במחרוזת מסויימת.

להלן דוגמה של איך ניתן לעבוד עם שתי הפונקציות הללו:

string myString = Console.ReadLine();
if (myString.StartsWith("Hello"))
{
    Console.WriteLine("Hello is at the start of the input");
}

if (myString.EndsWith("Hello"))
{
    Console.WriteLine("Hello is at the end of the input");
}

הפונקציה Replace

הפונקציה Replace מאפשרת לנו לבצע החלפה של כל המופעים של מחרוזת א' במחרוזת ב' בתוך המחרוזת שלנו.

לדוגמא, נניח שיש לנו מחרוזת את המחרוזת "Good morning, what a beautiful morning!", ונרצה להחליף את המילה "morning" במילה "night". 
נכתוב תוכנית שעושה זאת:

string before = "Good morning, what a beautiful morning!";
string after = before.Replace("morning", "night");

Console.WriteLine(before);
Console.WriteLine(after);

והתוצאה:

C# מחרוזות


פונקציות נוספות

הפונקציה IndexOf מחפשת מחרוזת א' בתוך מחרוזת ב' ומחזירה את המיקום שלה. 
הפונקציה Substring מאפשרת להעתיק חלק ממחרוזת לתוך מחרוזת אחרת. 
הפונקציה CompareTo מאפשרת להשוות בין שתי מחרוזות לפי סדר ההופעה שלהם במילון.

למחלקה string פונקציות עזר נוספות ומומלץ לבחון אותן בעצמכם.





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:27 לינק ישיר 
מדריך C# – תאריכים ועבודה עם DateTime

בחלק זה נלמד על טיפוס משתנה בשם DateTime, שיאפשר לנו לעבוד עם תאריכים.

הטיפוס DateTime

משתנה שיוגדר להיות מטיפוס DateTime יוכל להכיל בתוכו כל תאריך ושעה אפשרי. 
משתנה מטיפוס DateTime מכיל אוסף של פונקציות עזר שמותאמות במיוחד לעבודה עם תאריכים.

יצירה של משתנה מטיפוס DateTime

יצירת משתנה DateTime מתבצעת באמצעות המילה new שכבר ראינו לה שימוש בחלקים הקודמים למדריך. 
בעת היצירה ניתן (אך לא חובה) להעביר פרמטרים על התאריך ההתחלתי שיהיה בתוך המשתנה.

לדוגמא, קטע הקוד הבא יוצר משתנה מטיפוס DateTime המכיל את התאריך 12/12/2005 והשעה 09:00, ומדפיס אותו למסך:

DateTime date = new DateTime(2005, 12, 12, 9, 0, 0);
Console.WriteLine(date);

התוצאה:

מדריך #C - עבודה עם DateTime

קטע הקוד הבא יוצר משתנה מטיפוס DateTime ללא הגדרת הזמן, במקרה כזה הזמן מוגדר להיות 00:00:00

DateTime date = new DateTime(2005, 12, 12);
Console.WriteLine(date);

מדריך #C - עבודה עם DateTime


תכונות של DateTime

למשתנה מטיפוס DateTime יש תכונות נוחות המאפשרות לקבל את החלקים השונים מתוך התאריך או השעה.

לדוגמא, התכונה Year מחזירה את השנה של התאריך. 
באופן דומה קיימות תכונות Month , Day , Hour , Minute , Second ואפילו Millisecond שמחזירים את החודש, יום, שעה, שניה ואלפית השניה בהתאמה:

DateTime date = new DateTime(2005, 12, 12, 9, 0, 0);
Console.WriteLine(date);
Console.WriteLine("Year = " + date.Year.ToString());
Console.WriteLine("Month = " + date.Month.ToString());
Console.WriteLine("Day = " + date.Day.ToString());
Console.WriteLine("Hour = " + date.Hour.ToString());
Console.WriteLine("Minute = " + date.Minute.ToString());
Console.WriteLine("Second = " + date.Second.ToString());

מדריך #C - עבודה עם DateTime

 

מה השעה?

תכונה אחת מאוד שימושית במחלקה DateTime היא נקראת Now והיא מחזירה את התאריך והשעה הנוכחית.

לדוגמא, קטע הקוד הבא מציג את השעה:

DateTime currentTime = DateTime.Now;
Console.WriteLine(currentTime);

מדריך #C - עבודה עם DateTime

 

חיבור תאריכים

ניתן לבצע פעולות חיבור על מה משתנה מטיפוס DateTime. 
לדוגמא, אם נרצה לחשב מה יהיה התאריך בעוד 40 יום, נשתמש בפונקציה AddDays עם פרמטר 40:

DateTime currentTime = DateTime.Now;
DateTime future = currentTime.AddDays(40);
Console.WriteLine(currentTime);
Console.WriteLine(future);

והתוצאה:

מדריך #C - עבודה עם DateTime

שימו לב שהוספת 40 יום לתאריך נעשתה תוך התחשבות במספר הימים בחודש הנוכחי.

באופן דומה לפונקציה AddDays קיימות הפונקציות AddYears , AddMonths , AddHours , AddMinutes ,AddSeconds שמוסיפות שנים, חודשים, שעות, דקות ושניות בהתאמה.

הדפסת תאריכים בפורמטים שונים

הפונקציה ToString היא פונקציה שקיימת לכל משתנה בשפת C#, והיא מאפשרת להמיר כל אוביקט למחרוזת.

כאשר עובדים עם תאריכים נרצה לפעמים להדפיס את התאריך בפורמט שונה מברירת המחדל. ניתן לעשות זאת ע"י העברת פרמטר לפונקציית ToString. 
לדוגמא, קטע הקוד הבא מציג את השעה הנוכחית במספר פורמטרים שונים:

DateTime currentTime = DateTime.Now;
Console.WriteLine(currentTime.ToString());
Console.WriteLine(currentTime.ToString("dd/MM/yyyy"));
Console.WriteLine(currentTime.ToString("yyyy-MM-dd HH:mm"));

הרצת התוכנית תיתן לנו:

מדריך #C - עבודה עם DateTime





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:28 לינק ישיר 
מדריך C# – עבודה עם enum

בחלק זה נלמד כיצד ליצור טיפוסים חדשים מסוג enum. 

מהו טיפוס מסוג enum?

טיפוס מסוג enum הוא טיפוס שיכול לקבל ערכים רק מתוך קבוצה קטנה של ערכים. 
לדוגמא, ניתן להגדיר טיפוס מסוג enum בשם Month שיוכל לקבל רק אחד מהערכים הבאים:

כיצד עובדים עם enum?

ראשית יש להגדיר את הטיפוס החדש, משתמשים במילה enum בשביל לציין שזהו טיפוס מסוג enum, לאחר מכן נותנים את שם הטיפוס ולאחריו את רשימת הערכים מופרדת בפסיקים.

לדוגמא, הקוד הבא מגדיר enum בשם Month המכיל את ערכי החודשים השונים:

enum Month
{
    January,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}

נציין שהגדרת הטיפוס צריכה להיות מחוץ לפונקציית ה Main ואפילו מחוץ למחלקה Program שכן אנחנו מגדירים טיפוס חדש.

כעת נוכל להשתמש בטיפוס החדש בתוכנית הראשית:

static void Main(string[] args)
{
    Month myMonth = Month.December;
    Console.WriteLine(myMonth);
}

מתי נשתמש בenum?

השימוש בטיפוס מסוג enum עוזר לקריאות של התוכנית ומונע שגיאות.

לדוגמא, התבוננו בקטע הקוד הבא:

if ((documentState == 1) && (userSelection == 2))
{
    SaveDocument();
}
else if ((documentState == 1) && (userSelection == 1))
{
    DiscardDocument();
}

לא ברור מה הייתה כוונת המשורר, מה מייצגים המספרים 1,2 והאם יש להם משמעות יחידה. 
לעומת זאת, אותו הקטע תוך שימוש בenum יראה בצורה הרבה יותר ברורה:

if ((documentState == DocumentState.NotSaved) && 
    (userSelection == DocumentOperation.Save))
{
    SaveDocument();
}
else if ((documentState == DocumentState.NotSaved) &&
         (userSelection == DocumentOperation.Discard))
{
    DiscardDocument();
}

כאשר במקרה זה נגדיר שני טיפוסים מסוג enum באופן הבא:

enum DocumentOperation
{
    Load,
    Save,
    Discard
}

enum DocumentState
{
    Saved,
    NotSaved
}




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:28 לינק ישיר 
מדריך C# – מבנים (struct)

בחלק זה נלמד כיצד ליצור טיפוסים חדשים מסוג struct. 

מהו טיפוס מסוג struct?

טיפוס מסוג struct מאפשר לנו לאחד מספר משתנים בטיפוס אחד. 
בדרך כלל נרצה לעשות איחוד כזה כאשר למשתנים השונים יש משמעות לוגית כאשר הם ביחד. 

כיצד מגדירים טיפוס מסוג struct?

נראה דוגמה להגדרת טיפוס מסוג struct. 
נניח שאנו רוצים לייצג נקודה במישור. נצטרך לשמור שני נתונים מטיפוס double, ערך הנקודה בציר ה X וערך הנקודה בציר ה Y. 
במקום להגדיר זוג משתנים בכל פעם שנרצה נקודה, נוכל להגדיר טיפוס חדש מסוג struct שיאחד לנו שני משתנים תחתיו.

קטע הקוד הבא מגדיר טיפוס מסוג struct כדי לייצג אובייקט נקודה:

struct Point
{
    public double X;
    public double Y;
}

נעיר כי הגדרת ה- struct צריכה להיות מחוץ להגדרת המחלקה, שכן אנו מגידירים טיפוס חדש, בדומה להגדרתenum. 

כיצד משתמשים בstruct שהגדרנו?

השימוש ב- struct נעשה באופן דומה למשתנה רגיל. 
ראשית יש להקצות לו זיכרון בעזרת שימוש בפקודה new. 
לאחר מכן נוכל לפנות למשתנים של ה- struct שלנו (שנקראים גם שדות) ולכתוב ולקרוא את הערכים שלהם.

לדוגמא, להלן קטע קוד שמגדיר אוביקט נקודה ומציב בו ערכים במשתנים:

static void Main(string[] args)
{
    Point p1 = new Point();
    p1.X = 1;
    p1.Y = 2;
}

הגדרת struct מורכב

כעת שראינו כיצד להגדיר אוביקט מסוג נקודה, נוכל להגדיר אוביקט יותר מסובך כמו מלבן, שישתמש בהגדרת האוביקט נקודה הקיימת. 
נזכור שכל מלבן ניתן לייצג ע"י שתי נקודות: הנקודה השמאלית עליונה והנקודה הימנית תחתונה.

בהתאם לכך נגדיר טיפוס מסוג struct חדש בשם Rectangle שמכיל שני משתנים מסוג Point. 
בנוסף נראה כיצד אנו משתמשים באוביקט Rectangle החדש.

קוד התוכנית מובא כאן במלואו:

namespace StructDemo
{
    struct Point
    {
        public double X;
        public double Y;
    }

    struct Rectangle
    {
        public Point TopLeft;
        public Point BottomRight;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Rectangle rect = new Rectangle();
            rect.TopLeft.X = 0;
            rect.TopLeft.Y = 0;
            rect.BottomRight.X = 100;
            rect.BottomRight.Y = 100;
        }
    }
}




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:30 לינק ישיר 
מדריך C# - תכנות מובנה עצמים - מבוא

מאמר זה פותח סדרה של מאמרים בנושא תכנות מונחה עצמים (Object Oriented Programming). בניגוד לשאר המאמרים בסדרה זו, מאמר זה הינו תאורטי ואינו משלב קוד טכני ומטרתו היא להציג את הקונספט של תכנות מונחה עצמים.

שיטת Object Oriented – כל דבר הוא אובייקט

השיטה של תכנות מונחה עצמים (אובייקטים) הינה למעשה, חיקוי של חשיבת האדם, כלומר, היא אינטואיטיבית וקרובה יותר לדרך החשיבה של רוב האנשים המטפלים בבעיה העומדת בפניהם.

נסביר: בבואנו להסתכל על העולם – אנו באופן טבעי ואוטומטי מסווגים את כל העצמים על פי קטגוריות שונות. לדוגמא, העולם הפיזי מחולק על ידנו לחי, צומח ודומם. קיימת גם חלוקה פנימית יותר ובה את החי אנו מחלקים ליונקים, עופות, דגים וכך הלאה לחלוקות פנימיות יותר. כל עצם או מושג המוכר לנו בעולם הזה משתייך למספר רב של קטגוריות בסדר היררכי כלשהו.

למה? 
התשובה לכך פשוטה, כך קל ונח יותר לשלוט בכמות האדירה של האינפורמציה בה אנו מוצפים וכך יעיל ונח יותר לסדר אינפורמציה זו במוחנו, בכדי שנוכל לשלפה ולזהותה בקלות בכל עת שנחפוץ.

נראה דוגמא - בבואנו לספר לחבר כי רכשנו רכב חדש אין צורך לפרט לו כי רכשנו מנוע, קרבורטור(מאייד), מצמד, דוושת דלק, רדיאטור, הגה וכו'.... אלא בעצם הגדרת המושג רכב יידע החבר כי כל אלו נכללים, ומכאן שאין צורך בפירוט מצדנו וזה יתרון עצום! אחרת עבור כל רכב היינו צריכים לציין את כל מרכיביו, בעוד שבזכות העובדה כי אנו מסווגים ומקטלגים את הדברים כל שנצטרך לציין הן התכונות המייחדות את הרכב שלנו ולא את המשותפות לכלל הרכבים.

תכונה נוספת המאפיינת את תהליך החשיבה של האדם הינה – שיוך של פעולה לעצם כלשהו. למשל, נשווה בין שתי הפעולות הבאות: הדלקת נר והדלקת המחשב – לכאורה, מבחינה לוגית משמעותן של שתי הפעולות זהה – שתיהן פעולות "הדלקה", אך הנגזרת של כל פעולה היא מימוש פיזי אחר – כלומר, סידרת הצעדים אשר נבצע שונה לחלוטין בין פעולה אחת לאחרת.

ומכאן כשנתבקש לבצע פעולת "הדלקה" אנו נדע איזו פעילות עלינו לבצע על פי שיוך של הפעולה לעצם אותו אנו מתבקשים להדליק. כלומר, קיים קשר חשוב בין הפעולה לבין העצם עליו או איתו היא מתבצעת.

שני התהליכים החשיבתיים שהגדרנו אצל בני האדם מיושמים בצורה דומה בגישה מונחית העצמים ע"י יצירת טיפוס חדש המכונה Class (מחלקה) המאפשר ריכוז של מאפיינים (משתנים) ומטודות (פונקציות) תחת הגדרה אחת.

לאחר הגדרת המחלקה ניתן לייצר מופעים שלה, כל מופע של מחלקה קרוי Object (אובייקט). כאשר המחלקה הינה תבנית ליצירת אובייקטים מסוגה, כלומר, המחלקה מהווה את ההגדרה של המשתנים והשיטות שיכילו אח"כ כל מופעיה (האובייקטים מסוגה).

 

אלמנטים עיקריים של שיטת Object Oriented

כפי שהוסבר, השיטה המכוונת עצמים תומכת ובנויה ממחלקות ואובייקטים, אך קיימים בה מרכיבים עיקריים ומהותיים נוספים בבניית מערכות והם מהווים את משולש תכונותיה העיקריות:

clip_image002



כימוס – Encapsulation

כימוס, הנקרא לעיתים הסתרת מידע, מאפשר הסתרת כל המרכיבים הפנימיים של האובייקט ומאפשר גישה אליהם אך ורק דרך הפונקציות של האובייקט המהוות את הממשק. 
מכאן שאנו מגנים על מאפייני האובייקט באי מתן גישה ישירה אליהם, אלא אך ורק דרך פונקציות ממשק שהוגדרו לאותו אובייקט והן לא מאפשרות לבצע במאפייניו שינויים בלתי הולמים או חוקיים.

כימוס מספק את הגבול שבין מאפייניו הפנימיים של האובייקט לבין הממשק החיצוני שלו, בכך שהוא לא מאפשר שינוי באופן ישיר של משתני האובייקט, אלא רק דרך פונקציות הממשק שלו.

מושג נוסף אשר מרכיב את הכימוס הוא מושג ההפשטה (Abstraction):

הפשטה (Abstraction)

הפשטה המהווה חלק מהכימוס, מתייחסת לאופן שבו מיוצגת בעיה נתונה במרחב התוכנית. שפות התכנות לכשעצמן מספקות הפשטה. אנו התוכניתנים לא עוסקים בפנים של המחשב, כלומר, מרחב זיכרון, אוגרים ומחסניות של המעבד.

באופן כללי, תוכנית אינה אלא תיאור מופשט של הליך או תופעה, שמתקיימים או מתרחשים בעולם הממשי. אולם, הקשר בין ההפשטה ושפת התכנות הוא דו צדדי: מצד אחד משתמשים בשפת התכנות כדי לכתוב תוכנית המהווה הפשטה של העולם הממשי, ומצד שני משתמשים בשפת התכנות כדי לתאר בצורה מופשטת התנהגות פיזית של המחשב בו משתמשים (למשל על ידי שימוש במספרים עשרוניים במקום בייצוג בינארי שלהם, במשתנים במקום בכתובות מפורשות של תאי זיכרון וכדומה).

וכפי ששפות התכנות העיליות יוצרות הפשטה של ההתנהגות הפיזית של המחשב ומאפשרות להשתמש במרחבי הזיכרון שלו בצורה שקופה, מבלי צורך להעמיק בחומרה שמרכיבה אותו, כך אנו צריכים לתכנן את המחלקות שמשתתפות במערכת שלנו כדי שיאפשרו עבודה נוחה וברורה יותר ללקוחות המחלקות שלנו (=אותם תוכניתנים שייצרו אובייקטים מסוג המחלקות).

מכאן, שעל מנת לאפשר ללקוחות המחלקה שלנו להתמקד במטרה ולא לעסוק בפרטים הנוגעים לדרך הפעולה של המחלקה עצמה, עלינו לתכנן הפשטה של המחלקה בדרך הטובה ביותר. ממשק המחלקה הוא המימוש של ההפשטה!

נסביר את החשיבות בעזרת אנלוגיה למכשיר כספומט המוכר לנו מחיי היום יום. מכשיר זה אמור לקבל כקלט כרטיס וסיסמא תואמת ולאפשר ללקוח משיכת מזומנים או הצגת פרטי חשבון רלוונטיים. למכשיר הכספומט יש קבוצת פונקציות שעליו להציג בפני המשתמשים. פונקציות אלו מהוות את הממשק למשתמש. ממשק זה מתבטא בחריץ להעברת הכרטיס, חריץ להוצאת הכסף או פלט, צג, מקלדת וכו' – בעצם כל אחד מאלו מהווה חלק ממשק המכשיר.

מכשירי הכספומט לא השתנו הרבה מאז המצאתם, זאת משום שלמרות העובדה שהמערכת הפנימית שלהם השתנתה עם התפתחות הטכנולוגיה, הרי שהממשק הבסיסי שלהם לא היה צריך להשתנות בהרבה, ובכך איפשר למשתמשי הכספומט לעבוד באותה צורה לה הורגלו.

חלק אינטגרלי של תכנון ממשק מחלקה הוא הבנה עמוקה של מרחב הבעיה כולה. הבנה זו תעזור לנו ליצור ממשק המספק למשתמשים גישה למידע שהם זקוקים לו, אך מבודד אותם מהעבודה הפנימית של המחלקה. עלינו לתכנן מחלקה שתאפשר שינוי של מאפייני המחלקה בלי שתהיה לכך השפעה על קוד קיים.

בנוסף לכך, אל לנו להשתמש במונחים זרים ללקוחות המחלקה שלנו כי אם במונחים טבעיים אשר יהיו ברורים גם ללקוחות שהם לא מתמחים בנושא המנוהל ע"י המחלקה. כמו כן עלינו להגן על משתני המחלקה בצורה כזו שכל שינוי שלהם יהיה דרך פונקצית ממשק, בכדי שנהיה בטוחים כי ננקטו כל אמצעי הזהירות, דבר שלכאורה לא בהכרח יידעו לקוחות המחלקה לבצע, מטבע הדברים של חוסר בקיאות במחלקה שלנו.

לסיכום - עיצוב ההפשטה של מחלקות בדרך המועילה ביותר לתוכניתנים המשתמשים בהן הוא בעל חשיבות עליונה בעיצוב תוכנה הניתנת לשימוש חוזר (Reuse). ממשק יציב, סטטי ועקבי לאורך שינויי יישום עתידיים של המחלקה מחייב פחות שינויים עם הזמן.

ההפרדה בין המשתמש (התוכניתן המשתמש במחלקה שפיתחנו) לבין פרטי היישום - היא זו שהופכת מערכת שלמה ומורכבת לקלה יותר להבנה ולכן גם לאחזקה.

הורשה - Inheritance

הורשה היא הכלי שבאמצעותו ניתן להגדיר מחלקה במושגים של מחלקה אחרת ולא מאפס, כלומר, להגדיר מחלקה היורשת את מרכיביה (משתנים ופונקציות) ממחלקה אחרת.

על ידי הורשה יכול המתכנת להגדיר סוג של קשר בין מחלקה אחת לאחרת, באמצעות ההורשה ניתן לגזור מחלקה קיימת ובכך ליצור מחלקה חדשה אשר מכילה את הקיימת, בהמשך ניתן לשנות את המחלקה החדשה בדרך הרצויה.

המחלקה אותה אנו יורשים נקראת מחלקת הבסיס (Base Class) והמחלקה החדשה שנוצרה ע"י מחלקת הבסיס נקראת מחלקה נגזרת (Derived Class). המחלקה הנגזרת יורשת את כל החברים (משתנים ופונקציות) של מחלקת הבסיס.

יכולת זו מאפשרת חיסכון בכתיבת קוד ושימוש בקוד קיים. נדגים - אנו מתכננים מערכת לניהול אוניברסיטה כלשהי ומכאן שחלק מן הישויות המעורבות הן: סטודנטים, מרצים ועובדי מנהלה. עבור כל אחת מישויות אלו נצטרך לשמור את התכונות הבאות: שם פרטי, שם משפחה, תעודת זהות, כתובת וכו'. בנוסף, לכל אחת מהישויות נצטרך לשמור פרטים נוספים שונים המייחדים אותה – לדוגמא: שנת לימוד לישות סטודנט, תעריף לשעה לישות מרצה ומשכורת בסיס לישות עובד מנהלה.

אנו יכולים להגדיר כל ישות בנפרד על כל תכונותיה, אך בכך לכתוב קוד זהה המגדיר את התכונות המשותפות ופונקציות הממשק שלהן עבור כל ישות (מחלקה). פתרון זה יגרום לכתיבת קוד מיותרת, מה גם שכל שינוי במימוש של פונקצית ממשק יגרור צורך בשינוי בכל אחת מהמחלקות.

Without Inheritance

בתרשים זה מתוארות המחלקות ללא השימוש בהורשה, כאשר אנו רואים כאן את החזרה על התכונות המשותפות בכל מחלקה, כמו כן תהיה חזרה על פונקציות הממשק המנהלות תכונות אלו בכל מחלקה (לא מתואר בשרטוט).

פתרון אחר יעיל יותר, הן מבחינת חיסכון בקוד והן מבחינת ניהול עקבי שלו, הוא פיתרון בו נגדיר ישות אדם אשר תכיל את כל התכונות ופונקציות הממשק שמשותפות לכל הישויות, ובהמשך כל אחת משלושת הישויות תירש את מחלקת האדם (מחלקת הבסיס) ותוסיף לה את התכונות והפונקציות המייחדות אותה. 

with Inheritance
בתרשים זה מתוארות המחלקות עם שימוש בהורשה, כאשר אנו רואים שכל המידע המשותף מנוהל במחלקת בסיס אותה יורשות המחלקות הנגזרות. כל אחת מהמחלקות הנגזרות תוסיף על התכונות שהיא ירשה את התכונות המייחדות אותה ובנוסף תשנה ותוסיף את פונקציות הממשק הדרושות לה.

תכונת ההורשה שימושית גם במקרים בהם נרצה להוסיף תכונות או לשנות פעילות של Class קיים מבלי לפגוע ב- Class המקורי, או כאשר אין בידנו את הקוד של ה- Class המקורי (אלא קובץ לאחר קומפילציה) ואז הפתרון הוא הורשת המחלקה הקיימת.

דבר נוסף חשוב מאד, אותו נבין רק בתום קריאת הפרק הבא והוא שההורשה היא זו שמאפשרת את ה- Polymorphism.

רב-צורתיות - Polymorphism

בתחילה נעזר בדוגמא על מנת להמחיש את מטרת עקרון הרב-צורתיות. 
נניח ואנו מתכננים מערכת לניהול מוסך. המוסך שלנו מטפל ברכבים מהסוגים הבאים: אוטובוסים, רכבים פרטיים ומשאיות. בהתאם לתכונה ההורשה שלמדנו קודם אנו מניחים כי נגדיר מחלקת בסיס Car שתכיל את כל הנתונים המשותפים וממנה יגזרו 3 המחלקות: Truck, Private ו- Bus, כאשר כל אחת מהמחלקות תוסיף ותעדכן את התכונות הייחודיות לה. 
מחלקת Garage (מוסך) תכיל מבנה נתונים שינהל את כל הרכבים שנמצאים ברגע נתון מסוים בטיפול במוסך, נניח ומבנה הנתונים שלנו יוחזק בתוך מערך .

מכאן, שהמערך שלנו צריך להחזיק ולנהל אובייקטים מסוגים שונים (Truck, Private או- Bus)!

נסביר: רב-צורתיות מספקת יתרון בכך שהיא מעניקה את היכולת לקבץ אובייקטים להם יש מחלקת בסיס משותפת ולהתייחס אליהם בצורה עקבית.

אם נתבונן בדוגמא, נשים לב כי המשותף ל- 3 המחלקות (Truck, Private ו- Bus) היא מחלקת הבסיס Car, מכאן שכל אחת ממחלקות אלו מכילה בתוכה את מאפייני מחלקת Car ועל כן, כל אחת ממחלקות אלו היא גם מחלקת Car.

עיקרון ה- Polymorphism מאפשר להתייחס לכל המחלקות שנגזרו ממחלקת בסיס מסוימת כמחלקות מאותו סוג, הלא הוא מחלקת הבסיס, ובהתאם לכך ליצור מבנה נתונים של אובייקטים מסוג מחלקת הבסיס אשר יכול להכיל את האובייקטים מהסוגים השונים – מחלקת הבסיס ונגזרותיה. יש לציין כי בסיוע של המרה (casting) מתאימה או מנגנונים מיוחדים נוכל להתייחס לאובייקט מסוים במבנה הנתונים בהתאם לסוגו האמיתי (המחלקה שהוא מייצג מופע שלה) ולא רק כאל אובייקט מסוג מחלקת הבסיס.

למשל, כאשר נתאר מחלקה של יונקים (מחלקת הבסיס) נשים לב בוודאי כי "אכילה" היא פעולה חיונית לקיומם. כלומר, כל סוגי היונקים (מחלקות נגזרות) חייבים לדעת לבצע את הפונקציה Eat, מאידך כל אחד מהיונקים מייצג מימוש שונה לפונקציה Eat – הרי כל אחד אוכל דברים שונים בדרכים שונות. נניח כי אנו מחזיקים רשימה מקושרת של יונקים מהסוגים השונים (אפשרי להחזיק מבנה נתונים של אובייקטים שונים אם להם מחלקת בסיס משותפת).

ריבוי צורות היא האפשרות לקחת עצם (אובייקט) מטיפוס יונק (אחד כלשהו מתוך הרשימה) ולהורות לו לאכול (Eat) כאשר אותו עצם יבצע את הפעולה המתאימה לו ביותר – כלומר, תופעל הפונקציה Eat שהוגדרה עבורו! זוהי טכניקת תכנות המבצעת את הפונקציה של המחלקה המתאימה.

מבנה השפה המאפשר את ריבוי הצורות הוא הכריכה הדינמית (Dynamic Binding), 
כאשר כריכה דינמית (או מאוחרת) היא האפשרות לקשור בין קריאה לפונקציה כלשהי לבין מימוש כלשהו של הפונקציה בזמן ריצה ולא בזמן ה- Link.

אם נחזור לדוגמא קודמת אזי, כל המבנים ברשימה שלנו הם מטיפוס יונק, אך כל אחד הוקצה בזמן ריצה להיות יונק מסוג מסוים (אחת מהמחלקות הנגזרות), לכל אחד מהיונקים קיימת פונקצית Eat בעלת מימוש שונה. נניח ואנו מבצעים קריאה לפונקצית Eat של מבנה מסוים ברשימה– אם הכריכה לא היתה דינמית - אלא מתבצעת בשלב הקומפילציה - אזי היתה מופעלת הפונקציה Eat של מחלקת הבסיס, מאחר וזהו טיפוס מסוג יונק (בטרם הריצה לא ניתן לדעת איזה סוג יונק יוקצה עבור מבנה זה), בעוד שבכריכה דינמית הקשירה לפונקצית Eat המתאימה תתבצע בזמן ריצה – זמן בו כבר ידוע סוגו המדויק של המבנה ועל כן תופעל הפונקציה המתאימה. 

Polymorphism

בתרשים זה מתוארת היררכית המחלקות, כאשר בדוגמא זו התעלמנו ממאפייני כל מחלקה והמאפיינים המשותפים הנמצאים במחלקת הבסיס Mammal . ניתן לשים לב כי בכל אחת מהמחלקות מוגדרת פונקצית Eat בעלת מימוש שונה! (יש לציין כי במחלקת Mammal המימוש יכול להיות ריק או לא קיים כלל) .

נציג את הרשימה שהוזכרה בדוגמא לעיל, כאשר סוג טיפוס המצביעים מצביע ל- Mammal (כפי שהוסבר, כל המחלקות הנגזרות הן גם מסוג מחלקת הבסיס). כאשר, בזמן ריצת התוכנית כל אחד ממצביעים אלו הוקצה דינמית לטיפוס כלשהו מסוג המחלקות הנגזרות. ובזכות תכונת הכריכה המאוחרת (דינמית) המאפשרת את עיקרון ה- Polymorphism, כשנבצע קריאה לפונקציה Eat באמצעות צומת כלשהי ברשימה המקושרת – תופעל הפונקציהבהתאם לסוג ההצבעה (Cow/Person/Lion) ולא בהתאם לסוג המצביע (Mammal), משמע, מערכת זמן הריצה תבטיח קריאה לשיטה (פונקציה) מתאימה של האובייקט הנגזר.

יתרון חשוב נוסף של הרב צורתיות הוא שקוד ישן יכול להשתמש בקוד חדש. כלומר, גם אם נוסיף עכשיו סוג חדש של יונק שנגזר ממחלקת יונק, נוכל להכניס אותו לרשימה והקוד כולו ימשיך לפעול מבלי שהיה צורך לשנות אותו.

נסכם, עיקרון ה- Polymorphism מעניק את האפשרות להתייחס לאובייקטים מסוגים שונים באופן זהה, באם יש להם מחלקת בסיס משותפת. אפשרות זו יוצרת תוכניות מאד נוחות לקריאה, כתיבה ותחזוקה – ניתן להגדיר שיטות (פונקציות) שעל כל מחלקה לתת, ולהפעיל שיטות אלו על אובייקט מבלי לדעת מהו בעצם הסוג שלו. זוהי עוצמה תכנותית אדירה!



תוקן על ידי אנוני_מוס ב- 27/07/2011 11:34:35




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:35 לינק ישיר 
מדריך C# - תכנות מובנה עצמים - יצירת מחלקה בסיסית

מחלקה היא תבנית סגורה המכילה משתנים ופונקציות, אשר ניתן ליצור ממנה הרבה מופעים (אובייקטים). מחלקה מאפשרת לממש את עקרון הכימוס (encapsulation) - היכולת להסתיר אלמנטים הקיימים בתוך המחלקה, כך שרק לחברי המחלקה תהיה גישה אליהם.

כדי להוסיף מחלקה חדשה לפרויקט יש ללחוץ על לחצן העכבר הימני על הפרויקט (בחלון ה- Solution Explore) ולבחור Add –> Class. בחלון שיפתח יש לתת שם למחלקה. פעולה זו יוצרת מחלקה חדשה בקובץ חדש בפרויקט. ב C# ניתן גם לכתוב מספר מחלקות בקובץ אחד, אך לצורך התמצאות קלה בפרויקט זה לא מומלץ.





 נהוג להתחיל שמות של מחלקות ושמות של פונקציות באות גדולה באנגלית (למשל, Person).

 נהוג להתחיל שמות של משתנים באות קטנה באנגלית (למשל, firstName).

כמו שניתן לראות מבנה המחלקה הוא:

  class Person
  {
    //כל מה שנכתוב כאן שייך למחלקה
  }

יצירת מחלקה עם משתנים:

  class Person
  {
    public string firstName;
    public string lastName;
    public int id;
  }
 הערה: בד"כ משתנים יוגדרו כ- private וחשיפתם מחוץ למחלקה תתבצע באמצעות properties מתאימים. נראה זאת בהמשך.

יצירת אובייקט מהמחלקה:

Person p = new Person(); 

שימוש במשתנים להשמת ערכים:

p.FirstName = "Lior";
p.LastName = "Zamir";
p.Id = 1234578889;

שימוש במשתנים לשליפת ערכים:

Console.WriteLine("Name: {0} {1}", p.FirstName, p.LastName);

כתיבת פונקציות בתוך מחלקה:

class Person
{
  public string firstName;
  public string lastName;
  public int id;

  public void Print()
  {
    Console.WriteLine("Full Name: {0} {1}", firstName, lastName);
  }
}

הפעלת הפונקציה מהאובייקט:

p.Print();




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:35 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: שימוש ב- Properties

Property הינו מנגנון המאפשר לממש את עקרון הכימוס (encapsulation). הרעיון הוא להסתיר את המשתנים ולחשוף אותם דרך מנגנון סינון ובקרה לצורכי בדיקת תקינות המידע. בכדי להסתיר את המשתנה יש להגדיר אותו כ- private ואת ה- Property שמאפשר את הגישה למשתנה יש להגדיר כ- public. ניתן לגשת למשתנה private אך ורק מתוך המחלקה.

  נהוג ששם המשתנה יתחיל באות קטנה ושם ה- property יהיה אותו השם, אך יתחיל באות גדולה.


  class Person
  {
    //Data members:

    private string firstName;
    private string lastName;
    private int id;

    //Properties:

    public string FirstName
    {
      get { return firstName; }
      set { firstName = value; }
    }

    public string LastName
    {
      get { return lastName; }
      set { lastName = value; }
    }

    public int Id
    {
      get { return id; }
      set { id = value; }
    }

    //Methods:

    public void Print()
    {
      Console.WriteLine("Name: {0} {1}", firstName, lastName);
    }
  }

שימו לב שאין צורך לשנות את הפונקציה Print כי היא נמצאת בתוך המחלקה ולכן יכולה לגשת למשתני ה – private. לעומת זאת הקוד שעובד עם האובייקט חייב להשתנות כי אין לו גישה למשתנים:

      Person p = new Person();

      p.FirstName = "Lior";
      p.LastName = "Zamir";
      p.Id = 123456789;

      p.Print();

כאשר נכניס ערך לתוך ה- property יופעל החלק של ה - set:

      p.FirstName = "Lior";

וכאשר נקבל את הערך מה- property יופעל החלק של ה - get:

      string name = p.FirstName;

כעת ניתן לבצע סינון מידע כדי שלא יכנס מידע שגוי לאובייקט, לדוגמא נניח ונרצה לאכוף על הערך של id את החובה שלא יכיל יותר מ- 9 ספרות, נעשה זאת כך: 

    public int Id
    {
      get { return id; }
      set
      {
        if (value > 999999999)
          return;

        id = value;
      }
    }

הערה:

בשלב זה יש לבצע את הבדיקות ב- property ללא שימוש במנגנון Exception Handling, שילמד בהמשך.

ניתן ליצור גם Property שיכיל רק אחד מהחלקים get, set אם נרצה שלא לאפשר השמה או קבלה של הערך.

לדוגמא readonly property, אשר מאפשר רק לקבל את הערך, אך לא מאפשר לבצע השמה:

    public int Id
    {
      get { return id; }
    }

בדוגמא זו אם ננסה לבצע את הפעולה הבאה, נקבל שגיאת קומפילציה:

p.Id = 123456789;




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:35 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: שימוש ב- Constructors

בנאי (Constructor. נקרא גם בקיצור c'tor) הוא למעשה פונקציה לבנייה התחלתית של האובייקט. הוא מופעלאוטומטית בעת יצירת מופע מהמחלקה (אובייקט) באמצעות הפקודה new:

    Person p = new Person();

המטרה של בנאי היא לבצע פעולות בזמן יצירת האובייקט, כגון: אתחול משתנים.

מספר דברים שצריך לדעת לגבי בנאי:

  • שם הבנאי יהיה זהה לשם המחלקה המכילה אותו (case sensitive).
  • בנאי לא מחזיר ערך (גם לא void)
  • בנאי יכול לקבל פרמטרים
  • ניתן להעמיס (overload) בנאי (כך שלכל מחלקה יכולים להיות מספר בנאים. לכל הבנאים יהיה שם זהה, ההבדל יהיה במספר הפרמטרים שהבנאי מקבל ובסוגם).
  • ניתן לקרוא מבנאי אחד לאחר
  • בקריאה מבנאי אחד לאחר חובה לבצע זאת בשורה הראשונה של הבנאי
  • אם לא נכתוב אף בנאי במחלקה יהיה בנאי default (ברירת מחדל) שלא מקבל פרמטרים ולא מבצע שום פעולה.
  • מספיק שנכתוב בנאי אחד במחלקה (לא משנה עם איזה פרמטרים) אנו מוותרים על הבנאי ברירת מחדל.

דוגמא ליצירת בנאי שמקבל פרמטרים:

    public Person(string firstName, string lastName)
    {
      FirstName = firstName;
      LastName = lastName;
    }

שימו לב שההתייחסות היא למשתנה firstName של הפונקציה (המוגדר בכותרת הפונקציה) ולא של המחלקה. אם נרצה לגשת למשתנה המחלקה ניתן לכתוב: this.firstName

הפעלת הבנאי שמקבל פרמטרים:

      Person p = new Person("Lior", "Zamir");

יצירת בנאי המקבל פרמטרים ומעביר את חלקם לבנאי הקודם (שימו לב לשימוש במילת המפתח this):

   public Person(string firstName, string lastName, int id)
       : this(firstName, lastName)
   {
     Id = id;
   }

במקרה זה, הבנאי מקבל שלושה פרמטרים, מהם הוא שולח שניים לטיפול בבנאי הראשון, כך שנותר לו לטפל רק בפרמטר השלישי (id).





דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:36 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: אלמנטים סטטיים

משתנה סטטי

כאשר נגדיר משתנה בתוך מחלקה, הוא למעשה נוצר מספר פעמים, פעם אחת בכל אובייקט שניצור. לעיתים נרצה להגדיר משתנים אשר מוגדרים פעם אחת לכל האובייקטים מסוג מסוים. לדוגמא: משתנה שיכיל את מספר האובייקטים שנוצרו מסוג (מחלקה) מסוים. משתנה כזה נקרא משתנה סטטי (static).

  • משתנה סטטי הוא משתנה שיש לו רק מופע אחד בלבד והוא משותף לכל המחלקה (בניגוד למשתנה רגיל ה'משוכפל' עבור כל אובייקט מהמחלקה).
  • משתנה סטטי נמצא רק פעם אחת בזיכרון, לא משנה כמה מופעי אובייקטים יש למחלקה.
  • כדי להגדיר משתנה כסטטי יש להוסיף את המילה השמורה static בעת ההצהרה עליו.

להלן דוגמא להגדרת משתנה static אשר יכיל את מספר האובייקטים שנוצרו מהמחלקה Grade, כך שבכל פעם שניצור אובייקט (בתוך ה- c'tor) המשתנה יקודם ב- 1 (לצורך הפשטות מופיע רק הקוד הרלוונטי):


  class Grade
  {
    public static int counter;

    public Grade()
    {
      //...
      counter++;
    }
  }

בכדי לגשת מהתוכנית לערך של המשתנה הסטטי, יש לפנות אליו דרך שם המחלקה ולא דרך שם האובייקט כמו שפונים למשתנה רגיל. שימו לב, שאין צורך כלל ליצור אובייקט כדי לגשת למשתנה סטטי:

Console.WriteLine(Grade.counter);

מאפיין סטטי

באותה שיטה ניתן להגדיר כל אלמנט שלמדנו עליו בנושא מחלקות כסטטי:  Property, Method, C'tor.

כדי להגדיר מאפיין (Property) כסטטי יש להוסיף את המילה השמורה static בכותרת המאפיין. 

להלן דוגמא להגדרת static property . המאפיין הסטטי חושף את המשתנה הסטטי:

    private static int counter;

    public static int Counter
    {
      get { return counter; }
    }

גם כאן הגישה היא באותה שיטה (דרך שם המחלקה):

    Console.WriteLine(Grade.Counter);

פונקציה סטטית

פונקציה סטטית היא פונקציה שמופעלת על המחלקה כולה ולא על מופע בודד (אובייקט) של המחלקה.

  • כדי להגדיר פונקציה כסטטית יש להוסיף את המילה השמורה static בכותרת הפונקציה.
  • פונקציה סטטית שייכת למחלקה ולא לאובייקט (מופע) מסוים של המחלקה, לכן כדי לגשת לפונקציה סטטית דרך התוכנית הראשית נעשה זאת דרך שם המחלקה וציון הפונקציה (בניגוד לפונקציה לא-סטטית בה הגישה היא דרך שם האובייקט וציון הפונקציה).
  • דוגמאות לפונקציות סטטיות: חישוב מינימום, מקסימום, ממוצע, סכום, ספירה...

דוגמא לפונקציה סטטית המחשבת ממוצע (ולשם כך משתמשת במשתנים סטטיים):

  class Grade
  {
    //Static Data members:
    static int counter;
    static int sum;

    //Static Method:
    public static double Average()
    {
      return (double)sum / counter;
    }
  }

קריאה לפונקציה זו סטטית תתבצע כך:

Console.WriteLine(Grade.Average());


מחלקה סטטית

ניתן להגדיר גם static class, המשמעות היא:

  • כל מה שנמצא במחלקה חייב להיות static (משתנים, פונקציות, מאפיינים...)
  • לא ניתן ליצור מופעים (אובייקט) מהמחלקה
  • לא ניתן לרשת מהמחלקה

משתמשים במחלקה סטטית בעיקר ליצירת מחלקות עזר שיכילו פונקציות שירות. דוגמא למחלקות כאלו מתוך ה- NET Framework. :

  • Math – פונקציות וקבועים מתמטיים
  • Console – לעבודה עם מסך ה- Console




דדווח על תוכן פוגעני

סמל אישי
מנותק
נשלח ב-27/7/2011 11:36 לינק ישיר 
מדריך C# - תכנות מונחה עצמים: קבועים ( Constants ו- Readonly)

constants (קבועים) הם תאים בזיכרון אשר מכילים ערך קבוע שאינו ניתן לשינוי (בניגוד למשתנים שמטבעם כן יכולים להשתנות). קבועים ב – NET. למעשה מוגדרים אוטומטית כ- static (אין צורך לציין זאת במפורש), הדבר אומר שהגישה אליהם היא דרך שם המחלקה ולא דרך אובייקט.

הגדרת const:

  class MyConst
  {
    public const double PI = 3.14159;
  }

גישה ל - const:

   Console.WriteLine(MyConst.PI);

Readonly

readonly (קריאה-בלבד) הינו משתנה קבוע, אשר בניגוד ל- const אינו מוגדר כ- static אלא מוגדר עבור כל אובייקט. זאת אומרת שהערך שלו יכול להיות שונה בכל מופע של האובייקט, אך מרגע שנוצר האובייקט לא ניתן לשנות אותו. ניתן להכניס לתוכו ערך בשורת ההצהרה עליו (כלומר אתחול כמו const) ו / או בבנאי (c'tor) בלבד. לאחר האתחול ובניית המופע של המחלקה לא ניתן עוד לשנות את ערכו של משתנה זה.

להלן דוגמא להגדרת readonly והשמת ערך בבנאי.

  שימו לב שהפונקציה ()ChangeValue תגרור שגיאת קומפילציה כי ניתן לבצע השמה רק באתחול או בבנאי:

class TestReadonly
  {
    //read-only data-member:
    private readonly int val;

    //read-only Property:
    public int Val
    {
      get { return val; }
    }

    //Ctor:
    public TestReadonly(int v)
    {
      val = v;
    }

    //Method:
    public void ChangeValue()
    {
      val = 10;  // !שגיאת קומפילציה
    }
  } 


 שימו לב שבד"כ למשתנה read-only יהיה property שהוא read-only (כלומר, ללא חלק של set), מיכוון שלא ניתן לשנות את ערך המשתנה לאחר שהוא כבר נוצר ואותחל.

דוגמא לגישה למשתנה readonly - הגישה היא דרך שם האובייקט (בניגוד ל - const). ניתן לשים לב שבכל אובייקט יש ערך אחר למשתנה:

      TestReadonly t1 = new TestReadonly(10);
      TestReadonly t2 = new TestReadonly(12);

      Console.WriteLine(t1.Val);
      Console.WriteLine(t2.Val);




דדווח על תוכן פוגעני

סמל אישי
מנותק
   
בית > פורומים > אינטרנט ומחשבים > למתכנתים שבינינו > מדריך C#
מנהל לחץ כאן לנעילת האשכול
הוסף לעמוד האישי  דווח למנהל שלח לחבר
לדף הקודם 1 2 3 לדף הבא סך הכל 3 דפים.

bholext
2009 © כל הזכויות שמורות לבחדרי חרדים. קטגוריית אקטואליה וחדשות: עשרות פורומים הכוללים חדשות נעייס, מה קורה בחצרות חסידים, חדשות מחסידויות שונות בארץ ובעולם, דיונים בנושאי אקטואליה, פוליטיקה, בטחון ועוד.