Інколи хочеться просто писати код, який виконує роботу, і менше вникати в те, як саме ця робота виконується. Звичайно, що для цього є можливість створити свій клас і методи в загальному коді, після чого визивати їх в потрібний момент. Проте, часто зручніше використовувати узагальнені делегати (так, уже після самого слова делегати здається що буде складно).
Для того, щоб не страшити нікого словом делегат, я використовую слово функція. Мені здається, що воно також підходить під визначення того, що саме я хочу робити. А робити я хочу слідуюче – в одному блоці проєкту описую те, що хочу робити, а в іншому блоці мені потрібно мати можливість визивати цей код.
Значить в C# є три типи узагальнених делегатів, які підходять під вирішення моєї задачі. Поділяються вони по типу значення яке вони повертають.
Action – приймає на вхід параметри, просто виконується, і нічого не повертає
Predicate – приймає на вхід параметри, проводить порівняння і повертає тільки true або false
Func – приймає на вхід параметри, і повертає одне значення
Значить для того, щоб скористатись, потрібно написати сам код.
Приклад роботи з Action
В цьому прикладі просто буде виводитись повідомлення в лог, приймаючи на вхід текстовий рядок:
Action<string> ToLogText = (x) =>{ project.SendInfoToLog(x); }; ToLogText("Текст");
А в наступному прикладі на вхід можна подавати вже будь-який об’єкт, і він буде серіалізованим в json, після чого результат виведемо в лог ZennoPoster:
Action<object> ToLogJson = (x) =>{ project.SendInfoToLog(Global.ZennoLab.Json.JsonConvert.SerializeObject(x, Global.ZennoLab.Json.Formatting.None)); }; ToLogJson("Текст");
Інколи приходиться добавляти багато даних в один список, при чому приходиться думати про синхронізацію потоків – приблизно такий фрагмент позволяє вказати синхронізацію потоків один раз, а визивати цей код будь-де (випадок, коли список один і не хочеться вказувати його ім’я при кожному добавлені даних):
string list_name = "list"; Action<string> AddLine = (x) =>{ lock(SyncObjects.ListSyncer){ project.Lists[list_name].Add(x); } }; AddLine("рядок з текстом");
А ось приклад, коли є необхідність передавати ще ім’я списку, щоб добавляти рядок в потрібний список безпечним способом (тобто з синхронізацією потоків):
Action<string, string> AddLine = (x, y) =>{ lock(SyncObjects.ListSyncer){ project.Lists[x].Add(y); } }; string list_name = "list"; AddLine(list_name, "текст");
Приклад роботи з Predicate
Ось приклад C# коду, який об’являє предикат, який буде перевіряти пустий текстовий рядок чи ні повертаючи true коли пустий і false коли не пустий:
Predicate<string> CheckEmpty = (x) =>{ return string.IsNullOrEmpty(x); }; return CheckEmpty("---");
А ось приклад предиката, який порівнює число відносно 0 і повертає true тільки коли число більше 0:
Predicate<int> CheckX = (x) =>{ return x > 0; }; return CheckX(-5);
Такі фунції зручно використовувати, коли приходиться фільтрувати дані за допомогою LINQ з метою забрати з конструкції все лишнє для кращого розуміння послідовності виконання коду.
Приклад роботи з Func
Частенько буває так, що є необхідність передати аргументи в функцію, щось зробити з даними, і потім повернути вже результат в іншому вигляді. Для цього використовується узагальнений делегат Func.
Нижче приведений код на вхід одержує об’єкт, а на виході повертає текст у вигляді json:
Func<object, string> ToJson = (x) =>{ return Global.ZennoLab.Json.JsonConvert.SerializeObject(x, Global.ZennoLab.Json.Formatting.None); }; return ToJson("об'єкт");
Тобто, конструкції коду, які можуть займати багато місця, і мішати писати код – можна оформляти у вигляді таких функцій, і вже їх визивати в своєму проєкті.
Переносимо функції між блоками через project.Context
Для того, щоб передати такий узагальнений делегат з одного блока проєкту в інший використовується контекст. Виглядає це приблизно так:
project.Context["ToLogText"] = ToLogText; project.Context["CheckEmpty"] = CheckEmpty ; project.Context["CheckX"] = CheckX; project.Context["AddLine"] = AddLine; project.Context["ToJson"] = ToJson;
Це я описані вище фунції добавив в контекст, і тепер в будь-якому іншому блоці я можу визивати їх:
project.Context["ToLogText"]("виводжу рядок в лог");
Мені здається, що використання цих узагальнених делегатів є зручним у багатьох випадках при написанні своїх проєктів. А так як сам код знаходиться в блоках проєкту, то до коду завжди є прямий доступ – дуже легко відтестувати, і у випадку помилок ZennoPoster виведе правильну помилку (покаже де саме сталась помилка).
Якщо ще не приходилось користуватись – рекомендую однозначно спробувати і використовувати в своїх проєктах.