Делегат (програмування)
Делегат — форма вказівника на типобезпечну функцію, що використовується Common Language Infrastructure (CLI). Делегати вказують метод для виклику та, за бажанням, об'єкт, для якого буде викликано метод. Делегати використовують, серед іншого, для реалізації зворотних викликів та слухачів подій. Об'єкт делегата інкапсулює посилання на метод. Об'єкт делегата потім можна передати коду, який може викликати метод, на який посилаються, без необхідності знати під час компіляції, який метод буде викликано.
Багатоадресний делегат — це делегат, який вказує на кілька методів.[1]Багатоадресне делегування — це механізм, який забезпечує функціональність для виконання кількох методів. Існує внутрішньо підтримуваний список делегатів, і коли викликається багатоадресний делегат, виконується список делегатів.
У C# делегати часто використовують для реалізації зворотних викликів у подієво-орієнтованому програмуванні. Наприклад, делегат можна використати для позначення методу, який слід викликати, коли користувач натискає на певну кнопку. Делегати дають змогу програмісту повідомляти кілька методів про те, що сталася подія.
Приклад коду на C#
Код для оголошення об'єкта типу delegate з назвою SendMessageDelegate, який приймає параметр Message і повертає void:
delegate void SendMessageDelegate(Message message);
Код для визначення методу, який приймає як аргумент примірник делегата:
void SendMessage(SendMessageDelegate sendMessageDelegateReference)
{
// Синхронний виклик делегата та будь-яких інших приєднаних делегатів.
sendMessageDelegateReference(new Message("hello this is a sample message"));
}
Реалізований метод, який виконується під час виклику делегата:
void HandleSendMessage(Message message)
{
// Реалізація класів Sender та Message не стосується цього прикладу.
Sender.Send(message);
}
Код для виклику методу SendMessage, передаючи примірник делегата як аргумент:
SendMessage(new SendMessageDelegate(HandleSendMessage));
Делегати (C#)
delegate void Notifier(string sender); // Звичайний підпис методу з ключовим словом delegate
Notifier greetMe; // Змінна-делегат
void HowAreYou(string sender) {
Console.WriteLine("How are you, " + sender + '?');
}
greetMe = new Notifier(HowAreYou);
Змінна-делегат викликає пов'язаний метод, її викликають так:
greetMe("Anton"); // Викликає HowAreYou("Anton") та друкує "How are you, Anton?"
Змінні-делегати є об'єктами першого класу у формі new DelegateType(obj.Method): їм можна присвоїти будь-який сумісний метод або значення null. Вони зберігають метод та його приймач без будь-яких параметрів:[2]
new DelegateType(funnyObj.HowAreYou);
Об'єкт funnyObj може бути this і тоді його можна не вказувати. Якщо метод статичний, об'єктом має бути не примірником, а сам клас. Метод не може бути абстрактним, але може мати модифікатор new, override або virtual.
Щоб успішно викликати метод з делегатом, сигнатура методу має відповідати DelegateType з такою ж кількістю параметрів одного й того ж виду (ref, out, value) й однакового типу (включно з типом повернення).
Багатоадресні делегати (C#)
Змінна-делегат може містити кілька значень одночасно:
void HowAreYou(string sender)
{
Console.WriteLine($"How are you, {sender}?");
}
void HowAreYouToday(string sender)
{
Console.WriteLine($"How are you today, {sender}?");
}
Notifier greetMe;
greetMe = HowAreYou;
greetMe += HowAreYouToday;
greetMe("Leonardo"); // "How are you, Leonardo?"
// "How are you today, Leonardo?"
greetMe -= HowAreYou;
greetMe("Pereira"); // "How are you today, Pereira?"
Якщо багатоадресний делегат є функцією або не має параметра out, повертається параметр останнього виклику.[3]
Деталі реалізації
Хоча внутрішні реалізації можуть відрізнятися, примірники делегатів можна розглядати як кортеж об'єкта, вказівника методу та посилання (можливо, null) на інший делегат. Отже, посилання на один делегат може бути посиланням на кілька делегатів. Коли перший делегат закінчить роботу, якщо його ланцюгове посилання не є null, буде викликано наступний і так далі до закінчення списку. Цей шаблон дає змогу легко масштабувати механізм обробки події — від одного посилання до виклику списку делегатів — і широко використовується в CLI.
Продуктивність
Продуктивність делегатів раніше була значно нижчою, ніж виклик віртуальних або інтерфейсних методів (у 6-8 разів повільніше в бенчмарках Microsoft 2003 року),[4] але з моменту появи 2005 року .NET 2.0 CLR вона приблизно така ж, що й у викликах інтерфейсів.[5] Це означає, що порівняно з прямими викликами методів існують невеликі додаткові накладні витрати.
Правила побудови класів делегатів дуже суворі. Вони надають компіляторам-оптимізаторам значну свободу дій під час оптимізації делегатів, забезпечуючи при цьому безпеку типів.[джерело?]
Див. також
Примітки
- ↑ About Microsoft's "Delegates". Sun Developer Network. Sun Microsystems. Архів оригіналу за 10 лютого 1999.
- ↑ Mössenböck, Hanspeter (25 березня 2002). Advanced C#: Variable Number of Parameters (PDF). Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. с. 23—24. Процитовано 4 серпня 2011.
- ↑ Mössenböck, Hanspeter (25 березня 2002). Advanced C#: Variable Number of Parameters. Institut für Systemsoftware, Johannes Kepler Universität Linz, Fachbereich Informatik. с. 25. Процитовано 4 серпня 2011.
- ↑ Gray, Jan (Червень 2003). Writing Faster Managed Code: Know What Things Cost. Microsoft. Процитовано 9 вересня 2007.
- ↑ Sturm, Oliver (1 вересня 2005). Delegate calls vastly sped up in .NET 2. Процитовано 9 вересня 2007.
Посилання
- Документація MSDN для делегатів (англ.)
- Біла книга Sun про делегати (англ.)
- Відповідь Microsoft компанії Sun (англ.)
Джерело: стаття у Вікіпедії та історія редагувань (автори).