Перш за все, гарне запитання. Я захоплююсь вашою увагою до корисності, а не сліпо приймаю "найкращі практики". +1 для цього.
Я читав цей посібник раніше. Вам потрібно щось запам’ятати - це лише посібник, головним чином для новоприбулих на C #, які вміють програмувати, але не так добре знайомі зі способом C #. Це не стільки сторінка правил, скільки сторінка, яка описує, як зазвичай це робиться. А оскільки вони вже роблять це так скрізь, може бути хорошою ідеєю залишатися послідовними.
Я перейду до справи, відповідаючи на ваші запитання.
Перш за все, я припускаю, що ви вже знаєте, що таке інтерфейс. Щодо делегата, то достатньо сказати, що це структура, що містить набраний покажчик на метод, а також необов'язковий вказівник на об'єкт, що представляє this
аргумент для цього методу. У разі статичних методів останній покажчик є нульовим.
Також є делегати для багатоадресної передачі, які подібно до делегатів, але можуть мати декілька цих структур, призначених їм (мається на увазі один виклик для виклику на делегат багатоадресної передачі виклику всіх методів у призначеному списку викликів).
Що вони означають під шаблоном дизайну подій?
Вони мають на увазі використання подій у C # (який має спеціальні ключові слова для витонченої реалізації цієї надзвичайно корисної схеми). Події в C # проводяться делегатами з декількох груп.
Коли ви визначаєте подію, наприклад, у цьому прикладі:
class MyClass {
// Note: EventHandler is just a multicast delegate,
// that returns void and accepts (object sender, EventArgs e)!
public event EventHandler MyEvent;
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Компілятор фактично перетворює це в наступний код:
class MyClass {
private EventHandler MyEvent = null;
public void add_MyEvent(EventHandler value) {
MyEvent += value;
}
public void remove_MyEvent(EventHandler value) {
MyEvent -= value;
}
public void DoSomethingThatTriggersMyEvent() {
// ... some code
var handler = MyEvent;
if (handler != null)
handler(this, EventArgs.Empty);
// ... some other code
}
}
Потім ви підписуєтесь на подію, роблячи це
MyClass instance = new MyClass();
instance.MyEvent += SomeMethodInMyClass;
Який складається до
MyClass instance = new MyClass();
instance.add_MyEvent(new EventHandler(SomeMethodInMyClass));
Отже, це відбувається в C # (або .NET взагалі).
Як склад може виявитися легким, якщо використовується делегат?
Це легко продемонструвати:
Припустимо, у вас є клас, який залежить від набору дій, які слід йому передати. Ви можете інкапсулювати ці дії в інтерфейс:
interface RequiredMethods {
void DoX();
int DoY();
};
І кожен, хто хотів передати дії своєму класу, спочатку повинен реалізувати цей інтерфейс. Або ви могли б полегшити їхнє життя залежно від наступного класу:
sealed class RequiredMethods {
public Action DoX;
public Func<int> DoY();
}
Таким чином, абонентам потрібно лише створити екземпляр RequiredMethods і прив’язати методи до делегатів під час виконання. Це є , як правило , легше.
Такий спосіб робити надзвичайно вигідно при правильних обставинах. Подумайте над цим - навіщо залежати від інтерфейсу, коли все, що вам дійсно важливо, - це передача вам передачі?
Переваги використання інтерфейсів, коли є група споріднених методів
Корисно використовувати інтерфейси, оскільки інтерфейси зазвичай вимагають явних реалізацій часу компіляції. Це означає, що ви створюєте новий клас.
І якщо у вас є група споріднених методів в одному пакеті, вигідно, щоб цей пакет був повторно використаний іншими частинами коду. Тож якщо вони можуть просто інстанціювати клас замість того, щоб створити набір делегатів, це простіше.
Переваги використання інтерфейсів, якщо для класу потрібна лише одна реалізація
Як зазначалося раніше, інтерфейси реалізуються в час компіляції - це означає, що вони ефективніші, ніж виклик делегата (що таке рівень непрямості як такий).
"Одна реалізація" може означати реалізацію, яка існує єдиним чітко визначеним місцем.
Інакше реалізація може надходити з будь-якої точки програми, що, як правило, відповідає підпису методу. Це дозволяє досягти більшої гнучкості, оскільки методам потрібно лише відповідати очікуваному підпису, а не належати до класу, який явно реалізує певний інтерфейс. Але ця гнучкість може заробити і фактично порушує принцип заміни Ліскова , оскільки в більшості випадків ви хочете чіткості, оскільки це мінімізує шанс нещасних випадків. Так само, як статичне введення тексту.
Термін може також позначатись тут делегатами з кількома розсилками. Методи, оголошені інтерфейсами, можуть бути реалізовані лише один раз у класі реалізації. Але делегати можуть накопичувати кілька методів, які будуть називатися послідовно.
Отже, загалом, схоже, що довідник недостатньо інформативний, а просто функціонує як те, що він є - керівництвом, а не правилом. Деякі поради можуть насправді звучати трохи суперечливо. Ви самі вирішуєте, коли правильно застосовувати те, що. Посібник, здається, лише дає нам загальний шлях.
Я сподіваюся, що на ваші запитання відповіли на ваше задоволення. І знову кудо за питання.