Я використовую явну реалізацію інтерфейсу більшу частину часу. Ось основні причини.
Рефакторинг безпечніший
При зміні інтерфейсу краще, якщо компілятор може це перевірити. Це складніше з неявними реалізаціями.
На думку приходять два поширені випадки:
Додавання функції до інтерфейсу, коли вже наявний клас, який реалізує цей інтерфейс, має метод із тим же підписом, що і новий . Це може призвести до несподіваної поведінки, і мене кілька разів сильно вкусив. Важко "побачити" під час налагодження, оскільки ця функція, ймовірно, не розташована з іншими методами інтерфейсу у файлі (проблема самодокументування, згадана нижче).
Видалення функції з інтерфейсу . Неявно реалізовані методи будуть раптово мертвим кодом, але явно реалізовані методи потраплять через помилку компіляції. Навіть якщо мертвий код добре зберігати, я хочу примусити його переглянути та просувати.
Прикро, що у C # немає ключового слова, яке змушує нас позначати метод як неявну реалізацію, тому компілятор міг зробити додаткові перевірки. У віртуальних методах немає жодної з перерахованих вище проблем через необхідне використання "переопределення" та "нового".
Примітка: для фіксованих або рідко мінливих інтерфейсів (як правило, від API постачальника) це не проблема. Для власних інтерфейсів, однак, я не можу передбачити, коли / як вони зміняться.
Це самодокументація
Якщо я бачу "public bool Execute ()" у класі, знадобиться додаткова робота, щоб з'ясувати, що це частина інтерфейсу. Комусь, мабуть, доведеться прокоментувати це, сказавши так, або помістити його в групу інших реалізацій інтерфейсу, все під регіоном чи групуючим коментарем, що говорить "реалізація ITask". Звичайно, це працює лише в тому випадку, якщо заголовок групи не є екраном.
Тоді як: "bool ITask.Execute ()" є зрозумілим і однозначним.
Чітке розділення реалізації інтерфейсу
Я вважаю, що інтерфейси є більш «публічними», ніж публічні методи, оскільки вони створені, щоб відкрити лише трохи поверхні бетонного типу. Вони зводять тип до працездатності, поведінки, набору рис тощо. І при здійсненні, я думаю, корисно зберегти це розмежування.
Переглядаючи код класу, коли я стикаюся з явними реалізаціями інтерфейсу, мій мозок переходить у режим «кодового контракту». Часто ці реалізації просто переадресують інші методи, але іноді вони здійснюватимуть додаткову перевірку стану / парам, перетворення вхідних параметрів, щоб краще відповідати внутрішнім вимогам, або навіть перекладати для цілей версії (тобто декілька поколінь інтерфейсів, які все зводяться до загальних реалізацій).
(Я усвідомлюю, що паблік - це також кодові контракти, але інтерфейси набагато сильніші, особливо в інтерфейсі, котрий керується інтерфейсом, де пряме використання конкретних типів зазвичай є ознакою коду лише для внутрішніх справ.)
Пов'язане: Причина 2 вище Джон .
І так далі
Плюс переваги, про які вже говорилося в інших відповідях тут:
Проблеми
Це не все весело і щастя. Є деякі випадки, коли я дотримуюся імпліцитів:
- Типи цінності, тому що для цього знадобиться бокс і нижча perf. Це не суворе правило, і залежить від інтерфейсу та способу його використання. Ізрівнянний? Неявне. ІФОРМАТИВНО? Напевно, явний.
- Тривіальні системні інтерфейси, у яких є методи, які часто викликаються безпосередньо (наприклад, IDisposable.Dispose).
Крім того, може бути болісно робити кастинг, якщо ви насправді маєте конкретний тип і хочете викликати явний метод інтерфейсу. Я маю справу з цим одним із двох способів:
- Додайте публікації та запропонуйте їм методи інтерфейсу для впровадження. Зазвичай це відбувається з більш простими інтерфейсами при роботі всередині.
- (Мій кращий метод) Додайте
public IMyInterface I { get { return this; } }
(який має бути вбудованим) та зателефонуйте foo.I.InterfaceMethod()
. Якщо декілька інтерфейсів, які потребують цієї здатності, розгорніть ім’я за межами I (на моєму досвіді, у мене є рідкість, що я маю цю потребу).