24.11.2019

Delegate - Generics \ Collections


Delegate

Delegate - הוא טיפוס שמצביע לפונקציה

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



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

delegate int Calc(int x, int y);



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

קוד פשוט להבנת הבסיס של delegate

3 – יצירת delegate שמקבל 2 מספרים לא שלמים ומחזיר מספר

15-22 – יצרנו 2 פונקציות שמקבלות 2 מספרים ומבצעות חישוב ומחזירות ערך

8 – יצרנו אובייקט חדש של ה delegate שמקבל את הפונקציה הראשונה

9 – קריאה לפונקציה הראשונה באמצעות הפונקציה של ה delegate

11- החלפת הפונקציה שבתוך המצביע לפונקציה אחרת 

12 – קריאה שנית לפונקציה

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





1.  namespace ConsoleApplication15

2.  {

3.  delegate double MetOperator(double a, double b);

4.  class Program

5.  {

6.  static void Main(string[] args)

7.  {

8.  MetOperator fucAddress = new MetOperator(add);

9.  double sum = fucAddress (10, 5);

10. Console.WriteLine(sum);


11. fucAddress = new MetOperator(Sub);

12. double sub = fucAddress (15, 10);

13. Console.WriteLine(sub);

14. }

15. static double add (double a , double b)

16. {

17. return a + b;

18. }

19. static double Sub(double a, double b)

20. {

21. return a - b;

22. }

23. }

24. }



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

MetOperator fucAdd = (add);

fucAdd = (Sub);

המערכת יוצרת לבד את האובייקט


מדוע צריך delegates כאשר אפשרי לקרוא לפונקציה ?

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



Multicast delegate

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

הוספת פונקציה באמצעות +=



static void Main(string[] args)

        {
            MetOperator fucAddress;

            fucAddress = new MetOperator(add);

            double result = fucAddress (10 , 5 );

            Console.WriteLine(result);

            fucAddress += new MetOperator(Sub);

            result = fucAddress(10, 5);

            Console.WriteLine(result);
        }

        static double add (double a , double b)

        {
            return a + b;
        }

        static double Sub(double a, double b)

        {
            return a - b;
        }

בפעם הראשונה קראנו לפונקציה אחת וקיבלנו תשובה – בפעם השנייה המערכת מריצה את 2 הפונקציות ולכן התוצאה שונה (נקבל את התוצאה שלה פונקציה השנייה)





Generics \ Collections



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


המנגנון מאפשר כתיבת מתודות, מחלקות, ממשקים ו-
Delegates המגדירים "טיפוס כללי", או לחילופין להשתמש במחלקות הכתובות כבר והן חלק מה- Framework.


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

נ.ב לעבודה גנרית נדרשת הספרייה  using System.Collections.Generic;VS נוסף באופן אוטומטי)




Collections

דוג' פשוטה לפונקציה גנרית – הפונקציה יכולה לקבל כל ערך באמצעות ההגדרה <T> ובכל קריאה מגדירים מה סוג הערך שתקבל



namespace ConsoleApp6

{
    class person

    {
        public int id { get; set; }

        public void stem<T>( T a , T b)

        {
            Console.WriteLine(a.ToString() + b.ToString());
        }
       
    }

    class Program

    {
        static void Main(string[] args)

        {
            int x = 8;

            int y = 11;

            person p1 = new person();

            p1.stem<int>(x, y);

            p1.stem<string>("hii - ", "dodo");
        }
    }



דוג' נוספת – כל list הוא מחלקה גנרית שביצירה מגדירים את הסוג של אובייקטים שיכיל



List  יכול לקבל גם יותר מסוג אחת של אובייקט באמצעות הגדרת רשימה של אובייקטים

namespace ConsoleApp6

{
    class person

    {
        public int id { get; set; }

        public string noame { get; set; }


        public person(int id , string name)

        {
            this.id = id;
            this.noame = noame;
        }

}
    class Program

    {
        static void Main(string[] args)

        {
           List<person> listGenerics = new List<person>();

            listGenerics.Add(new person(1, "dodo"));

            listGenerics.Add(new person(2, "nosha"));
        }
    }







מחלקה גנרית

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



namespace ConsoleApp6

{
    class Generic<T>

    {
        public T id;

        public T name;

        public void print(T a, T b)

        {
            Console.WriteLine($"{ a.ToString()} - {b}");
        }

        public Generic(T id , T name)

        {
            this.id = id;

            this.name = name;       
        }
    }

    class Program

    {
        static void Main(string[] args)

        {
            Generic<string> g1 = new Generic<string>("aa " ,"avi");

            g1.print(g1.id , g1.name);


            Generic<int> g2 = new Generic<int>(123, 444);

            g2.print(g2.id, g2.name);
        }
    }
}






List.sort() - IComparable

בכל List מספרי ישנה פונקציה שמסדרת אותו (גם ליסט שמקבל string או char אבל גם שם זה בגלל הערך המספרי של האותיות). כל מה שעלינו לעשות הוא לכתוב list.sort() והרשימה תסתדר.

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

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







    public class Player :IComparable<Player>

    {
        public string Name { get; set; }

        public int Total { get; set; }


        public Player(string name, int total)

        {
            Name = name;

            Total = total;
        }


        public int CompareTo(Player obj)

        {
            int result = 0;

            Player f = obj as Player;

            if (Total > f.Total) result = 1;

            if (this.Total < f.Total) result = -1;

            return result;
        }
    }

    class Program

    {
        static void Main(string[] args)

        {
            List<Player> lst = new List<Player>();

            lst.Add(new Player("John", 100));

            lst.Add(new Player("Smith", 120));

            lst.Add(new Player("Cait", 97));

            lst.Sort();

            foreach (var t in lst)

            {
                Console.WriteLine(t.Name + " - " + t.Total);
            }
        }



QUEUE - Stack

2 אוספים נוספים  שמאפשרים שמירת אוסף נתונים

  1. בשונה LIST רגיל בתור ניתן לשמור כל סוג של אובייקט
  2. ניתן לגשת ישירות לראש\סוף התור ללא ציון המיקום ברשימה (מה שמחייב לבדוק מה האורך)



QUEUE – תור

עובד בשיטת  First in first out - FIFO )מי שנכנס ראשון יוצא ראשון)

  • Enqueuer - פונקציה להכנסת אברים לסוף התור
  • Peek פקודה להחזרת הערך שתחילת התור – ללא להוציא אותו
  • Dequeuer לקבלת הערך שבתחילת התור - מוציא אתו מהרשימה
  • Count   - לספירת האברים בתור
  • Clear - לניקוי התור
  • Contains חיפוש אלמנט בתור (אם קיים או לא)
קוד שמדגים את כל הפונקציות שלמדנו ב QUEUE



public class Program

    {
        static void Main(string[] args)

        {
            Queue my_queue = new Queue();


            my_queue.Enqueue("noam");

            my_queue.Enqueue("dodo");

            my_queue.Enqueue(1);

            my_queue.Enqueue(100);

            my_queue.Enqueue(null);

            my_queue.Enqueue(2.4);


            foreach (var ele in my_queue)

            {
                Console.WriteLine(ele);
            }

            Console.WriteLine($"Total elements present in my_queue: {my_queue.Count}");

            Console.WriteLine($"Total elements present in my_queue: {my_queue.Dequeue()}");

            Console.WriteLine($"Total elements present in my_queue: {my_queue.Count}");

            Console.WriteLine($"Total elements present in my_queue: {my_queue.Peek()}");

            Console.WriteLine((my_queue.Contains("dodo") == true) ?  ("Element available...!!") : ("Element not available...!!"));

            my_queue.Clear();

            Console.WriteLine($"Total elements present in my_queue: {my_queue.Count}");
        }
    }






Stack – ערימה (מחסנית)

עובד בשיטת LIFO - Last in first out (מי שנכנס אחרון הראשון יוצא) התור הוא חד כיווני ולכן האחרון שנכנס הוא שיצא ראשון
פקודות
  • Push – להכניס ערכים לרשימה
  • Pop – להוציא את האחרון שנכנס
  • Peek – לקבל את האחרון נכנס
  • Contains חיפוש אלמנט בתור (בדוג' שלנו יחזיר True or False)

קוד שמדגים את כל הפונקציות שלמדנו ב QUEUE





            Stack myStack = new Stack();

            myStack.Push("Element 1 ");

            myStack.Push("Element 2 ");

            myStack.Push(4.3);

            myStack.Push(443);

            Console.WriteLine($"Total number of elements in the Stack are : {myStack.Count}");

            foreach (var ele in myStack)

            {
                Console.WriteLine(ele);
            }

            Console.WriteLine("Element at the top is : " + myStack.Peek());

            myStack.Pop();

            Console.WriteLine($"Total number of elements in the Stack are : {myStack.Count}");

            Console.WriteLine(myStack.Contains("noam"));

            myStack.Clear();

            Console.WriteLine($"Total number of elements in the Stack are : {myStack.Count}");








אין תגובות:

הוסף רשומת תגובה

MVC

Web api Front end (צד משתמש) שולח http request     כל אתר מכל מכשיר יכול להתחבר ולקבל נתונים (אין אפליקציה) ולא משנה באיזו שפה ה...