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

מדריך #C - שלישי

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

נלקח מהאתר: www.webmaster.org.il

מדריך #C מתקדם - Generics

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

דוגמאות:

  • נרצה מחלקה Point המייצגת נקודה במישור בעלות יכולות, כמו למשל להזיז את הנקודה, כאשר לעיתים נרצה שהאורדינאטות (x,y) של הנקודה יהיו מסוג int ולעיתים נרצה שיהיו מסוג double (או מכל סוג אחר – long, byte, decimal...)
  • מחלקה לניהול רשימה של נתונים (Collection) עם יכולת להוסיף ולמחוק איברים כאשר בכל פעם נרצה לשים שם נתונים אחרים

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

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

מיד לאחר שם המחלקה לדוגמא:

 

  class Point <T>
  {
   ...
  }

 

לאחר מכן נוכל להשתמש בפרמטר זה בתוך המחלקה:

 

 class Point<T>
  {
    private T x;
    private T y;

    public T X
    {
      get { return x; }
      set { x = value; }
    }

    public T Y
    {
      get { return y; }
      set { y = value; }
    }

    public Point(T x, T y)
    {
      X = x;
      Y = y;
    }

    public override string ToString()
    {
      return string.Format("X = {0}, Y = {1}", X, Y);
    }
  }

כאשר ניצור את האובייקט נספק לו את הפרמטר של הטיפוס גם כן בסוגריים משולשים. לדוגמא Point כאשר ה- T הוא מסוג int:

      Point<int> pInt = new Point<int>(10, 5);

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

    static void Main(string[] args)
    {
      Point<int> pInt = new Point<int>(10, 5);
      Point<string> pString = new Point<string>("a", "b");
      pInt.X = "a";
      pString.X = 10;
    }

עוד נקודות חשובות לגבי Generics:

  • ניתן לקבל יותר מפרמטר אחד עבור מספר סוגים של משתנים.
  • ניתן לבנות כמעט כל אלמנט שאנו מכירים כ- generics כגון: class, struct, method, delegate, interface
  • ניתן לרשת מחלקה generics ולהגדיר לה את הטיפוסים כקבועים

 דוגמא להורשת מחלקה generics:

 

  class PointDouble : Point<double>
  {

  }

בספריות של NET. ישנם הרבה טיפוסים מוכנים המממשים את הרעיון של Generics וזמינים לשימושינו, לדוגמא:

  • <IComparable<T - למימוש IComparable עם הפרמטר המתאים לפונקציה CompareTo
  • <List<T - דומה ל ArrayList אך עם הגדרת הטיפוס של הנתונים ביצירת האובייקט
  • <Dictionary<TKey, TValue - דומה ל- Hashtable עם הגדרת הסוג של ה- key ושל ה- value ביצירת האובייקט


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



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



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




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

סמל אישי
מנותק
נשלח ב-27/7/2011 11:09 לינק ישיר 
מדריך #C מתקדם - Generics: Constraints – התניות על פרמטרים גנריים

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

הפונקציה בדוגמא הבאה, מקבלת 2 פרמטרים גנריים. הראשון חייב להיות struct היורש משני הממשקים IComparable, IFormattable. הפרמטר השני חייב להיות class היורש מהממשק ICloneable:


    public void MethodWithConstrain<T, K>(T a, K b)
      where T : struct, IComparable, IFormattable
      where K : class, ICloneable
    {
      Console.WriteLine(a);
      Console.WriteLine(b);
    }

דוגמא נוספת – שימו לב ל- 11 ההעמסות של פונקצית Max של מחלקת Math: 

Math.Max

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

    public static T Max<T>(T a, T b)
        where T : IComparable
    {
      if (a.CompareTo(b) > 0)
        return a;

      return b;
    }

פונקציה זו יכולה להשוות בין שני פריטים כלשהם, מכל class/struct קיים/עתידי כל עוד הוא עונה לקריטריון הבא: יורש מהממשק IComparable.

Console.WriteLine(Max<int>(3, 5));
Console.WriteLine(Max<double>(9.5, 3.6));
Console.WriteLine(Max<string>("Hi", "Bye"));
Console.WriteLine(Max<DateTime>(DateTime.Now,
                                new DateTime(2011, 2, 1)));
Console.WriteLine(Max<Person>(p1, p2));

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

הפלט:

פלט - הפונקציה הג'נרית MAX

באופן זה ניתן להגביל גם את הטיפוסים במחלקות (נחייב את הטיפוס לממש את IComparable):

  class Point <T>
    where T : IComparable
  {
    …
  }




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

סמל אישי
מנותק
נשלח ב-27/7/2011 11:11 לינק ישיר 
מדריך #C מתקדם - אוספים גנריים - Generic Collections

ב- Net. קיימים מספר אוספים הנמצאים ב- namespace הנקרא System.Collections. להלן דוגמא למספר אוספים נפוצים:

  • ArrayList – מייצג אוסף דינמי של פריטים.
  • Hashtable – מייצג אוסף של זוגות ערכים (key/value) הממויין לפי ה- hash code של ה- key.
  • SortedList – מייצג אוסף של זוגות ערכים (key/value) הממויין לפי הערך של ה- key.
  • Queue – מייצג אוסף של פריטים בתור (הראשון שנכנס לאוסף, הוא הראשון שייצא – FIFO).
  • Stack – מייצג אוסף של פריטים במחסנית (האחרון שנכנס לאוסף, הוא הראשון שייצא – LIFO).

לשימוש באוספים אלו שני חסרונות עיקריים:

  1. ביצועים – שימוש באוספים הנ"ל יכול לגרום לביצועים נמוכים כאשר אנו מכניסים לאוסף טיפוסים מסוג struct בשל הפעולות בזיכרון המתבצעות בזמן ריצה ע"י המנגנונים של Net. (בתהליך הנקרא boxing שבו מתייחסים ל- value-type כגון struct כ- reference type). כלומר, פגיעה במהירות זמן הריצה של התוכנית.
  2. המרות – האוספים הרגילים אינם סוגים בטוחים (Typed-Safe) מיכוון שהם עובדים עם מחלקת Object. זה אומנם מקנה להם את היכולת להכיל כל טיפוס אך מנגד יוצר בעיה שהאוסף יכול לקבל טיפוסים שאנחנו לא רוצים שיהיו בו. וכמובן שכל שליפה של פריטים מהאוסף תאלץ אותנו להשתמש בהמרות מיותרות. הפיתרון הנהוג היה ליצור מחלקות אוסף משלנו ולממש אותם לטיפוס מסויים - פיתרון שמצריך המון עבודה.

וכאן נכנס לתמונה השימוש ב- Generics (הקיים החל מגירסה 2 של .Net): שימוש באוספים גנריים.

לכל האוספים הקלאסיים המוזכרים לעייל יש גירסה גנרית שניתן להשתמש בה. האוספים נמצאים ב- namespace הנקרא System.Collections.Generic (מופיע כברירת מחדל ב- using בראש של כל קובץ C# החל מגירסה 2).

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

לדוגמא, על-מנת ליצור אוסף גנרי דינמי (המסוגל לגדול/לקטון בהתאם לצורך) המתנהג כמו האוסף הקלאסי ArrayList אך ללא החסרונות שציינו, ניתן להשתמש באוסף הגנרי <>List  :

List<int> numbers = new List<int>();

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

האוסף מכיל את כל הפונקציונליות הרלוונטית (הוספה/הסרת פריטים, פונקציות שירות כמו מיון וכו'):

List<int> numbers = new List<int>();

numbers.Add(500);
numbers.Add(-25);
numbers.Add(17);

numbers.Sort();

Console.WriteLine("The numbers (After a Sort):");
foreach (int n in numbers)
  Console.WriteLine(n);

Console.WriteLine("Count: {0} numbers", numbers.Count);

הפלט:

פלט - שימוש באוסף הגנרי - List





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

סמל אישי
מנותק
נשלח ב-27/7/2011 11:11 לינק ישיר 
מדריך #C מתקדם – נציגים - Delegates

נציגים (delegates) ואירועים (events) הינם נושאים מאד מרכזיים בעבודה ב- NET. . הם מאפשרים למפתח לבנות רכיב בלי להגביל את מי שמשתמש בו לפעולות מסוימות.

delegate הוא טיפוס המצביע על פונקציה.

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

להלן דוגמא שננתח אותה מיד:


namespace Delegates
{
  delegate int Calc(int x, int y);

  class Program
  {
    static int Add(int a, int b)
    {
      return a + b;
    }

    static void Main(string[] args)
    {
      Calc c= new Calc(Add);
      int result = c(4, 6);

      Console.WriteLine(result);
    }
  }
}

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

להלן הגדרת delegate בשם Calc:

delegate int Calc(int x, int y);

 

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

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

Calc c = new Calc(Add);

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

int result = c(4, 6);
Console.WriteLine(result);

Multicast delegate

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

namespace Delegates
{
  delegate int Calc(int x, int y);

  class Program
  {
    static int Add(int a, int b)
    {
      Console.WriteLine("Add");
      return a + b;
    }

    static int Sub(int a, int b)
    {
      Console.WriteLine("Sub");
      return a - b;
    }

    static void Main(string[] args)
    {
      Calc c;
      c = new Calc(Add);
      c += new Calc(Sub);
      int result = c(4, 6);

      Console.WriteLine(result);
    }
  }
}

בפלט התוכנית ניתן לראות שהופעלו 2 הפונקציות אך התשובה שהוחזרה היא התשובה של הפונקציה האחרונה שהופעלה:

פלט - Add/Sub delegate

עד כאן הבנו איך יוצרים ומשתמשים ב- delegate להלן דוגמא הממחישה למה צריך delegate (מיד ננתח אותה):

namespace SortWithDelegate
{
  public delegate int CompareDeleg(int a, int b);

  class Program
  {
    static int[] arr;

    static int CompareAsc(int x, int y)
    {
      return x - y;
    }

    static int CompareDesc(int x, int y)
    {
      return y - x;
    }

    static void Sort(CompareDeleg compareMethod)
    {
      for (int i = 0; i < arr.Length - 1; i++)
        for (int j = i + 1; j < arr.Length; j++)
          if (compareMethod(arr[i], arr[j]) > 0)
            Replace(i, j);
    }

    private static void Replace(int i, int j)
    {
      int tmp = arr[i];
      arr[i] = arr[j];
      arr[j] = tmp;
    }

    static void PrintArr()
    {
      foreach (int num in arr)
        Console.Write(num + ",");

      Console.WriteLine();
    }

    static void Main(string[] args)
    {
      arr = new int[] { 123, 200, -63, 2, 7612, -13, 8 };
      Console.WriteLine("Orginal nuumbers:");
      PrintArr();

      Sort(new CompareDeleg(CompareAsc));
      Console.WriteLine("\nAscending order:");
      PrintArr();

      Sort(new CompareDeleg(CompareDesc));
      Console.WriteLine("\nDescending order:");
      PrintArr();
    }
  }
}

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

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

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

  • מספר חיובי – המספר הראשון יותר גדול
  • מספר שלילי – מספר השני יותר גדול
  • אפס – המספרים שווים

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

  • CompareAsc – למיון עולה
  • CompareDesc – למיון יורד

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

פלט - Sorting delegate





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

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

event – הודעה שאובייקט שולח לאובייקטים אחרים.

event הינו למעשה מקרה פרטי של delegate. הרעיון של event הינו להצביע על פונקציה והוא מספק מספר הגבלות אשר לא קיימות ב- delegate רגיל:

  • ניתן להגדיר event רק כחבר מחלקה (member) ולא ניתן בתוך פונקציה או כפרמטר לפונקציה
  • ניתן להפעיל event רק באותה מחלקה בה הוא נכתב
  • ניתן להוסיף ולהוריד פונקציות מ- event באמצעות =+=-  (כמו multicast delegate), אך לא ניתן לדרוס את מה שיש שם באמצעות =

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

בכדי להגדיר event צריך תחילה להגדיר delegate, לדוגמא:

 public delegate void SpeedDelegate(int newSpeed);

לאחר מכן יש להגדיר משתנה מסוג ה- delegate בצרוף המילה השמורה event:

 public event SpeedDelegate SpeedChanged;

רישום פונקציה ל- event (הצבעה) והפעלת ה- event הינם בדיוק כמו ב- delegate (למעט ההגבלה של =).

להלן דוגמא שמיד ננתח:

 public delegate void SpeedDelegate(int newSpeed);
 class Car
{
    public event SpeedDelegate SpeedChanged;

    private int speed;

    public int Speed
    {
      get { return speed; }
    }

    public void AddSpeed(int delta)
    {
      speed += delta;
      if (SpeedChanged != null)
        SpeedChanged(speed);
    }
  }
 class Program
{
    static void Main(string[] args)
    {
      Car c = new Car();

      //רישום הפונקציה לאירוע
      c.SpeedChanged += new SpeedDelegate(PrintSpeed);

      c.AddSpeed(16);
      c.AddSpeed(28);
      c.AddSpeed(52);
      c.AddSpeed(-10);
      c.AddSpeed(14);

    }
    static void PrintSpeed(int newSpeed)
    {
      Console.WriteLine(newSpeed);
    }
  }

בדוגמא זו ישנה מחלקה בשם Car המאפשרת שינוי מהירות הרכב. למחלקה event בשם SpeedChanged אשר מודיע כאשר השתנתה מהירות הרכב. בתוכנית רשמנו את הפונקציה PrintSpeed ל- event ובכל פעם שנוסיף מהירות (5 פעמים במקרה שלנו) ה- event יפעיל את הפונקציה הזו שמדפיסה את המהירות החדשה.

פלט התוכנית:

פלט - שימוש באירוע להצגת המהירות

השימוש באירועים נפוץ ב- .NET בעיקר בסביבות עשירות ב- GUI, למשל, WinForms, WPF, ASP.NET וכו'. השימוש באירועים בסביבות אלו מאפשר, למשל, להפעיל פונקציות המתבצעות כתוצאה מפעולה של המשתמש. לדוגמא, אירוע של לחיצה על כפתור יגרור תגובה בצורה של פונקציה המסוגלת לבצע אוסף של פעולות.

 public partial class MyForm : Form
  {
    Button myButton;

    public MyForm()
    {
      InitializeComponent();
      //הרישום לאירוע
      myButton.Click += new EventHandler(myButton_Click);
    }

    //הפונקציה המתבצעת כתוצאה מהפעלת האירוע
    private void myButton_Click(object sender, EventArgs e)
    {
      MessageBox.Show("Test");
    }
  }

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





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

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

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

כל attribute שניצור וכל attribute שכבר קיים ב-NET., הוא למעשה מחלקה (class) היורשת בצורה ישירה או עקיפה ממחלקה הנקראת Attribute.

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

[AttributeName] 
Some element

לדוגמא, attribute בשם Obsolete מעל הפונקציה Print:

[Obsolete]
static void Print()
{
  Console.WriteLine("Print");
}

ניתן להעביר פרמטרים ל- attribute במידה והוא מקבל לדוגמא:

[Obsolete("This method is obsolete, please use other method")]
static void Print()
{
Console.WriteLine("Print");
}

כשנקמפל את הפרויקט נקבל את האזהרה הבאה:

Obsolete Warning

דוגמא נוספת ל- attribute:

כאשר נרצה לתת attribute ל- assembly נכתוב זאת בקובץ AssemblyInfo.cs שנמצא בספריה Properties שבפרויקט ונעשה זאת כך:

[assembly: AssemblyTitle("MyApplication")]

AssemblyTitle מייצג כותרת ל- assembly שתוצג במאפייני הקובץ במערכת ההפעלה.




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

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

    ב- #C ניתן להעמיס פונקציות, בנאים ואופרטורים.

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

    התחביר להעמסת אופרטורים מתחלק ל- 2 סוגים:

    • אופרטור אונרי (Unary) – הפועל רק על אופרנד אחד, כגון ++a
    • אופרטור בינארי (Binary) – הפועל על 2 אופרנדים, כגון a+b

    העמסת אופרטור אונרי:

        public static Circle operator ++(Circle c)
        {
          //מימוש     
        }

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

    1. public static
    2. הערך המוחזר (Circle בדוגמא הנ"ל)
    3. מילת המפתח operator
    4. האופרטור שרוצים להעמיס (++ בדוגמא)
    5. פרמטר מטיפוס המחלקה ( Circle c בדוגמא)

    העמסת אופרטור בינארי:

        public static Circle operator +(Circle c, int x)
        {
          //מימוש     
        }

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

    1. public static
    2. הערך המוחזר (Circle בדוגמא הנ"ל)
    3. מילת המפתח operator
    4. האופרטור שרוצים להעמיס (++ בדוגמא)
    5. פרמטר מטיפוס המחלקה Circle c)בדוגמא)
    6. פרמטר נוסף, מטיפוס כלשהו (int x בדוגמא)

    לדוגמא, מימוש האופרטור ++ (אונרי) והאופרטור + (בינארי) במחלקה Circle:

    class Circle
      {
        public double Radius { get; set; }

        public static Circle operator ++(Circle c)
        {
          c.Radius++;
          return c;
        }

        public static Circle operator +(Circle c, int x)
        {
          Circle newC = new Circle();
          newC.Radius = c.Radius + x;
          return newC;
        }
      }

    שימוש באופרטורים:

      class Program
      {
        static void Main(string[] args)
        {
          Circle c = new Circle() { Radius = 10 };

          c++;
          Console.WriteLine(c.Radius);

          c = c + 5;
          Console.WriteLine(c.Radius);
        }
      }

    פלט התוכנית:

    פלט - העמסת אופרטורים

    האופרטורים הניתנים להעמסה:


    אופרטורים בינאריים

    +

    -

    *

    /

    %

    |

    ^

    >>

    <<

    &

    אופרטורים אונריים

    +

    -

    ++

    --

    !

    ~

    true

    false

    אופרטורים השוואתיים /לוגיים

    ==

    =!

    <

    >

    =<

    =>


    האופרטורים שלא ניתנים להעמסה:


    אופרטורים מיוחדים

    =

    .

    :?

    &&

    ||

    אופרטורים מילות-מפתח

    new

    is

    sizeof

    typeof

    checked

    unchecked

    אופרטורים מורכבים

    =+

    =-

    =*

    =/

    =%

    סוגריים

    (

    )

    [

    ]

    {

    }


     

    כללים להעמסת אופרטורים

    • מוגדרים תמיד כ- public static
    • העמסה של אופרטור השוואתי (לוגי) מחייבת העמסה של האופרטור המנוגד (כולל true,false). לדוגמא, העמסה של == תחייב העמסה גם של =!
    • העמסה של אופרטור השוואתי תחזיר ערך בוליאני בלבד
    • העמסה של אופרטורים ==, =! מחייבים מימוש הפונקציה Equals ופונקציה זו מחייבת מימוש הפונקציה GetHashCode
    • בכדי למנוע בילבול לא מומלץ לשנות את המשמעות המקורית (והמובנת) של האופרטור

     

    העמסת casting

    ניתן להעמיס גם את האופרטורים המבצעים casting בין טיפוסים. לדוגמא נרצה לאפשר המרה בין string ל- Circle ובחזרה, ובין double ל- Circle ובחזרה.

    תזכורת:

    • explicit casting – המרה מפורשת שבה חובה לבצע כתיבת ה- casting
    • implicit casting – המרה מרומזת ללא צורך בכתיבת ה- casting

    מימוש המרות מפורשות (explicitly casting) – 

    • המרה מפורשת מ- string ל- Circle :
        public static explicit operator Circle(string str)
        {
          Circle c = new Circle();
          c.Radius = double.Parse(str);
          return c;
        } 
    • המרה מפורשת מ-double ל- Circle :
        public static explicit operator Circle(double dbl)
        {
          Circle c = new Circle();
          c.Radius = dbl;
          return c;
        }

    מימוש המרות מרומזות (implicitly casting) - 

    • המרה מרומזת מ- Circle ל- string:
        public static implicit operator string(Circle c)
        {
          return c.Radius.ToString();
        }
    • המרה מרומזת מ- Circle ל- double:
        public static implicit operator double(Circle c)
        {
          return c.Radius;
        }

    שימוש:

       static void Main(string[] args)
        {
          string str = "9";
          //explicitly convert String to Circle:
          Circle c1 = (Circle)str;
          Console.WriteLine(c1.Radius);

          double dbl = 7;
          //explicitly convert Double to Circle:
          Circle c2 = (Circle)dbl;
          Console.WriteLine(c2.Radius);

          c1 += 10;
          str = c1; //implicitly convert Circle to String
          Console.WriteLine(str);

          c2 += 10;
          dbl = c2; //implicitly convert Circle to Double
          Console.WriteLine(dbl);
        }

    פלט התוכנית:

    פלט - העמסת casting





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

    סמל אישי
    מנותק
    נשלח ב-27/7/2011 11:30 לינק ישיר 

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



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

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

    bholext