Rozumiem, jak pracować z interfejsami i implementacją jawnego interfejsu w języku C #, ale zastanawiałem się, czy ukrywanie niektórych elementów, które nie byłyby często używane, jest złym rozwiązaniem. Na przykład:
public interface IMyInterface
{
int SomeValue { get; set; }
int AnotherValue { get; set; }
bool SomeFlag { get; set; }
event EventHandler SomeValueChanged;
event EventHandler AnotherValueChanged;
event EventHandler SomeFlagChanged;
}
Wiem z góry, że wydarzenia, choć przydatne, bardzo rzadko byłyby wykorzystywane. Pomyślałem więc, że warto ukryć je przed klasą implementacyjną poprzez jawną implementację, aby uniknąć bałaganu IntelliSense.
c#
interfaces
implementations
Kyle Baran
źródło
źródło
Odpowiedzi:
Tak, ogólnie jest to zła forma.
Jeśli twój IntelliSense jest zbyt zagracony, twoja klasa jest za duża. Jeśli twoja klasa jest zbyt duża, prawdopodobnie robi zbyt wiele rzeczy i / lub źle zaprojektowana.
Napraw swój problem, a nie objaw.
źródło
Użyj funkcji do tego, do czego była przeznaczona. Ograniczanie bałaganu przestrzeni nazw nie jest jednym z nich.
Uzasadnione powody nie polegają na „ukrywaniu się” i organizowaniu, lecz na rozwiązywaniu niejednoznaczności i specjalizacji. Jeśli zaimplementujesz dwa interfejsy definiujące „Foo”, semantyka dla Foo może się różnić. Naprawdę nie jest to lepszy sposób na rozwiązanie tego, z wyjątkiem jawnych interfejsów. Alternatywą jest uniemożliwienie implementacji nakładających się interfejsów lub zadeklarowanie, że interfejsy nie mogą powiązać semantyki (co zresztą jest po prostu kwestią konceptualną). Obie są słabymi alternatywami. Czasami praktyczność przebija elegancję.
Niektórzy autorzy / eksperci uważają to za kłopot funkcji (metody nie są tak naprawdę częścią modelu obiektowego typu). Ale podobnie jak wszystkie funkcje, ktoś go potrzebuje.
Ponownie nie użyłbym tego do ukrywania się lub organizacji. Istnieją sposoby na ukrycie członków przed intellisense.
źródło
IDisposable.Dispose
coś robi, ale ma inną nazwęClose
, to uważam, że jest to całkowicie rozsądne dla klasy, której umowa wyraźnie wyklucza stwierdzenie, że kod może tworzyć i porzucać instancje bez wywoływaniaDispose
jawnej implementacji interfejsu do zdefiniujIDisposable.Dispose
metodę, która nic nie robi.Mogę być oznaką niechlujnego kodu, ale czasami prawdziwy świat jest bałagan. Jednym z przykładów .NET Framework jest ReadOnlyColection, który ukrywa ustawiającą mutację [], Dodaj i Ustaw.
IE ReadOnlyCollection implementuje IList <T>. Zobacz także moje pytanie do SO kilka lat temu, kiedy się nad tym zastanawiałem.
źródło
ConcurrentDictionary
implementacja różnych elementówIDictionary<T>
jawnie, tak aby konsumenciConcurrentDictionary
A) nie dzwonili do nich bezpośrednio, a B) mogą korzystać z metod, które wymagają implementacji,IDictionary<T>
jeśli jest to absolutnie konieczne. Np. UżytkownicyConcurrentDictionary<T>
powinni zadzwonićTryAdd
zamiastAdd
unikać niepotrzebnych wyjątków.Add
jawne wdrożenie zmniejsza również bałagan.TryAdd
można zrobić wszystko, coAdd
można zrobić (Add
jest zaimplementowane jako wezwanie doTryAdd
, więc zapewnienie obu byłoby zbędne). JednakTryAdd
koniecznie ma inną nazwę / podpis, więc nie jest możliwe wdrożenieIDictionary
bez tej nadmiarowości. Jawna implementacja rozwiązuje ten problem w czysty sposób.Jest to dobra forma, aby zrobić to, gdy członek jest bezcelowy w kontekście. Na przykład, jeśli utworzysz kolekcję tylko do odczytu, która implementuje się
IList<T>
przez delegowanie do obiektu wewnętrznego,_wrapped
możesz mieć coś takiego:Tutaj mamy cztery różne przypadki.
public T this[int index]
jest zdefiniowany przez naszą klasę, a nie przez interfejs, a zatem nie jest oczywiście jawną implementacją, choć zauważ, że jest podobny do odczytu-zapisuT this[int index]
zdefiniowanego w interfejsie, ale jest tylko do odczytu.T IList<T>.this[int index]
jest jawny, ponieważ jedna jego część (getter) jest idealnie dopasowana do powyższej właściwości, a druga część zawsze zgłasza wyjątek. Chociaż jest to niezbędne dla kogoś, kto uzyskuje dostęp do instancji tej klasy za pośrednictwem interfejsu, nie ma sensu, aby ktoś używał jej za pośrednictwem zmiennej typu klasy.Podobnie, ponieważ
bool ICollection<T>.IsReadOnly
zawsze będzie zwracać wartość true, absolutnie bezcelowe jest pisanie kodu przeciw typowi klasy, ale może mieć to zasadnicze znaczenie przy używaniu go przez typ interfejsu, dlatego też implementujemy go jawnie.I odwrotnie,
public int Count
nie jest jawnie zaimplementowany, ponieważ może potencjalnie być użyteczny dla kogoś, kto korzysta z instancji za pośrednictwem własnego typu.Ale w twoim przypadku „bardzo rzadko używanym” bardzo mocno skłaniałbym się ku nieużywaniu wyraźnej implementacji.
W przypadkach, w których zalecam użycie jawnej implementacji wywołanie metody przez zmienną typu klasy byłoby albo błędem (próba użycia indeksowanego setera), albo bezcelowym (sprawdzenie wartości, która zawsze będzie taka sama), więc ukrywanie chronisz użytkownika przed błędnym lub nieoptymalnym kodem. To znacznie różni się od kodu, który Twoim zdaniem jest rzadko używany. W tym celu mógłbym rozważyć użycie tego
EditorBrowsable
atrybutu, aby ukryć członka przed inteligencją, choć i tak byłbym zmęczony; mózgi ludzi mają już własne oprogramowanie do filtrowania tego, co ich nie interesuje.źródło
IsReadOnly
właściwość?