Kategorie szkoleń | Egzaminy | Kontakt
  • 1
  • 2
  • 119

Odpowiedź (1)

  • 13

Typ delegatu (Delegate) pozwala na oddzielenie wywołania metody od miejsca jej wskazania. Pozwalają na luźniejsze powiązanie między kodem wywołującym a kodem definiującym, co powinno zostać wywołane (jaka metoda) przy zachowaniu metadanych - liczby oraz typu parametrów oraz typu wartości zwrotnej.

Nasze rozważania rozpoczniemy od uproszczonego przykładu - w aplikacji potrzebujemy funkcji logującej komunikaty - funkcja taka powinna przyjmować parametr tekstowy z komunikatem i nie powinna zwracać żadnej wartości. W tym celu budujemy i wywołujemy metodę LogMessage:

 

    class Program
    {
        static void Main(string[] args)
        { 
	LogMessage("start");             
	LogMessage("running");             
	LogMessage("stop");
        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
    }

 

Wywołując metodę LogMessage w programie, używamy wywołania po nazwie funkcji, co oznacza, że np. przy zastąpieniu funkcji LogMessage  funkcją LogMessageToFile musielibyśmy zmienić wszystkie odwołania w aplikacji. Aby uniknąć tej sytuacji, możemy użyć typu delegatu deklarując go początkowo w aplikacji i przypisując wywołanie poprzez zadeklarowany delegat:

 

    class Program
    {
        delegate void LogMsgDelegate(string m);
        static void Main(string[] args)
        {
            LogMsgDelegate log = LogMessage;
            log("start");
            log("running");
            log("stop");

        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
    }

 

W powyższym przykładzie słowo kluczowe delegate zostało użyte do zdefiniowania typu delegatu przyjmującego jeden parametr typu string i nie zwracającego wartości (void). Typ delegatu pozwala na prostą modyfikację metody logowania i użycie LogMessageToFile:

 

    class Program
    {
        delegate void LogMsgDelegate(string m);
        static void Main(string[] args)
        {
            LogMsgDelegate log = LogMessageToFile;
            log("start");
            log("running");
            log("stop");

        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
        static void LogMessageToFile(string mesage)
        {
            System.IO.File.AppendAllText(@"D:\tmp\log.txt", mesage + Environment.NewLine);
        }
    }

 

Załóżmy, że w naszej aplikacji chcielibyśmy równocześnie logować komunikaty na konsolę i do pliku, w takiej sytuacji możemy dodać kolejną metodę do naszej aplikacji - LogMessageAll - wywołującą istniejące metody logowania oraz przypisać ją do zmiennej delegacyjnej log:

 

    class Program
    {
        delegate void LogMsgDelegate(string m);
        static void Main(string[] args)
        {
            LogMsgDelegate log = LogMessageAll;

            log("start");
            log("running");
            log("stop");

        }
        static void LogMessageAll(string message)
        {
            LogMessage(message);
            LogMessageToFile(message);
        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
        static void LogMessageToFile(string mesage)
        {
            System.IO.File.AppendAllText(@"D:\tmp\log.txt", mesage + Environment.NewLine);
        }

 

Nie zawsze jednak w aplikacji chcemy tworzyć kolejną składową tylko po to, aby wywołać kombinację już istniejących, zamiast tego możemy użyć słowa kluczowego delegate do utworzenia metody anonimowej zastępującej pełną metodę:

 

    class Program
    {
        delegate void LogMsgDelegate(string m);
        static void Main(string[] args)
        {
            LogMsgDelegate log = delegate(string message)
            {
                LogMessage(message);
                LogMessageToFile(message);
            };

            log("start");
            log("running");
            log("stop");

        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
        static void LogMessageToFile(string mesage)
        {
            System.IO.File.AppendAllText(@"D:\tmp\log.txt", mesage + Environment.NewLine);
        }
    }

 

W ten sposób zastąpiliśmy metodę LogMessageAll metodą anonimową, korzystając ze słowa kluczowego delegate i zgodnej z typem delegatu definicji tej metody (z jednym parametrem string i nie zwracającej wartości). Przykład ten pokazuje, iż słowo kluczowe delegate poza deklaracją typu delegatu pozwala również na definiowanie metod anonimowych. Można powiedzieć, że metoda anonimowa skróciła zapis wywołania w stosunku do pełnej metody. Samą metodę anonimową możemy również zastąpić wyrażeniem lambda nieznacznie modyfikując kod przypisania zmiennej log:

 

class Program
    {
        delegate void LogMsgDelegate(string m);
        static void Main(string[] args)
        {
            LogMsgDelegate log = (string message) =>
            {
                LogMessage(message);
                LogMessageToFile(message);
            };

            log("start");
            log("running");
            log("stop");

        }
        static void LogMessage(string message)
        {
            Console.WriteLine(message);
        }
        static void LogMessageToFile(string mesage)
        {
            System.IO.File.AppendAllText(@"D:\tmp\log.txt", mesage + Environment.NewLine);
        }
    }

 

Podsumowując, można stwierdzić, że słowo kluczowe delegate pozwala na deklarację typów delegatów oraz definiowanie metod anonimowych. Metody anonimowe oraz ich uproszczona konstrukcja w postaci wyrażeń lambda pozwalają na uproszczenie definicji typów przez ograniczenie ilości nazwanych składowych. Typy delegacyjne uożliwiają luźne powiązania między metodami oraz ich wywołaniami, a także ułatwiają utrzymywanie złożonych aplikacji.

  • Odpowiedział
  • @ | 04.03.2015
  • TRENER ALTKOM AKADEMII