Najczęściej używam jawnego wdrożenia interfejsu. Oto główne powody.
Refaktoryzacja jest bezpieczniejsza
Podczas zmiany interfejsu lepiej jest, jeśli kompilator może go sprawdzić. Jest to trudniejsze w przypadku implementacji niejawnych.
Przychodzą mi na myśl dwa typowe przypadki:
Dodanie funkcji do interfejsu, w którym istniejąca klasa, która implementuje ten interfejs, ma już metodę z taką samą sygnaturą jak nowa . Może to prowadzić do nieoczekiwanego zachowania i kilka razy mnie ugryzł. Trudno jest „zobaczyć” podczas debugowania, ponieważ funkcja ta prawdopodobnie nie znajduje się przy innych metodach interfejsu w pliku (problem z samodokumentowaniem wymieniony poniżej).
Usuwanie funkcji z interfejsu . Metody niejawnie zaimplementowane będą nagle martwym kodem, ale metody jawnie zaimplementowane zostaną złapane przez błąd kompilacji. Nawet jeśli martwy kod dobrze jest zachować, chcę zostać zmuszony do jego przeglądu i promocji.
Szkoda, że C # nie ma słowa kluczowego, które zmusza nas do oznaczenia metody jako implementacji niejawnej, więc kompilator może wykonać dodatkowe kontrole. W metodach wirtualnych nie występuje żaden z powyższych problemów z powodu wymaganego użycia „zastępowania” i „nowego”.
Uwaga: w przypadku stałych lub rzadko zmieniających się interfejsów (zazwyczaj z interfejsów API dostawców) nie stanowi to problemu. Jednak w przypadku moich własnych interfejsów nie mogę przewidzieć, kiedy / jak się zmienią.
To jest samodokumentowanie
Jeśli zobaczę „public bool Execute ()” w klasie, zajmie to dodatkową pracę, aby dowiedzieć się, że jest to część interfejsu. Ktoś prawdopodobnie będzie musiał to skomentować, mówiąc o tym, lub umieścić go w grupie innych implementacji interfejsu, wszystko pod regionem lub komentarzem grupującym z napisem „implementacja ITask”. Oczywiście działa to tylko wtedy, gdy nagłówek grupy nie jest poza ekranem.
Natomiast: „bool ITask.Execute ()” jest jasne i jednoznaczne.
Wyraźne oddzielenie implementacji interfejsu
Myślę, że interfejsy są bardziej „publiczne” niż metody publiczne, ponieważ są one zaprojektowane tak, aby odsłonić tylko odrobinę powierzchni określonego typu betonu. Redukują typ do możliwości, zachowania, zestawu cech itp. A we wdrażaniu myślę, że warto zachować tę separację.
Kiedy patrzę na kod klasy, kiedy natrafiam na jawne implementacje interfejsu, mój mózg przechodzi w tryb „kontraktowania kodu”. Często te implementacje po prostu przekazują inne metody, ale czasami będą przeprowadzać dodatkowe sprawdzanie stanu / parametrów, konwersję przychodzących parametrów w celu lepszego dopasowania do wymagań wewnętrznych, a nawet tłumaczenie dla celów wersjonowania (tj. Wiele generacji interfejsów sprowadzających się do typowych implementacji).
(Zdaję sobie sprawę, że publikacje publiczne są również kontraktami kodowymi, ale interfejsy są znacznie silniejsze, szczególnie w bazie kodu opartej na interfejsie, w której bezpośrednie użycie konkretnych typów jest zwykle oznaką kodu wyłącznie wewnętrznego).
Powiązane: Powód 2 powyżej autorstwa Jona .
I tak dalej
Plus zalety wspomniane już w innych odpowiedziach tutaj:
Problemy
To nie tylko zabawa i szczęście. W niektórych przypadkach trzymam się implicitów:
- Typy wartości, ponieważ będzie to wymagało boksu i niższej perf. To nie jest ścisła zasada i zależy od interfejsu i sposobu, w jaki ma być używany. IComparable? Domniemany. IFormattable? Prawdopodobnie wyraźne.
- Trywialne interfejsy systemowe, które mają metody często wywoływane bezpośrednio (np. IDisposable.Dispose).
Ponadto rzutowanie może być uciążliwe, gdy w rzeczywistości masz konkretny typ i chcesz wywołać jawną metodę interfejsu. Radzę sobie z tym na jeden z dwóch sposobów:
- Dodaj publikacje i przekaż im metody interfejsu do wdrożenia. Zwykle dzieje się to z prostszymi interfejsami podczas pracy wewnętrznej.
- (Moja preferowana metoda) Dodaj
public IMyInterface I { get { return this; } }
(należy wstawić) i wywołaj foo.I.InterfaceMethod()
. Jeśli wiele interfejsów wymaga tej umiejętności, rozwiń nazwę poza ja (z mojego doświadczenia wynika, że mam taką potrzebę).