2009-02-23 8 views
27

Rozwijam zestaw klas , które implementują wspólny interfejs . Konsument mojej biblioteki oczekuje, że każda z tych klas będzie implementować pewien zestaw funkcji statycznych. Czy mimo to mogę udekorować te klasy, aby kompilator złapał przypadek, w którym jedna z funkcji nie jest zaimplementowana.Czy istnieje sposób zmusić klasy C# do realizacji niektórych funkcji statycznych?

Wiem, że w końcu zostanie złapany podczas budowania kodu konsumującego. A także wiem, jak obejść ten problem, używając rodzaju fabryki.

Po prostu ciekawa, czy istnieje jakaś składnia/atrybuty, które wymagają statycznych funkcji na zajęciach.

Ed Usunięto słowo "interfejs", aby uniknąć nieporozumień.

+0

Wybacz moją ciekawość, ale nie mogę przestać się zastanawiać, dlaczego potrzebujesz takiej funkcji? Co mają metody statyczne, które nie mają zwykłe metody instancji (niestatyczne)? Nic nie przychodzi mi do głowy. Chcesz podzielić się pewnym kontekstem na swoje pytanie? – Dan

+2

Nie potrzebujesz instancji, aby wywołać metodę statyczną. Są więc przyjemne w użyciu jako metody fabryczne, na przykład metoda dekantalizacji. – tpower

+0

@Dan: Dodatkowym zastosowaniem byłaby możliwość korzystania z interfejsu bez wiedzy, czy pracujesz nad instancją lub typem (lub zmieniając to później). Pomyśl o klasie statycznej, która reprezentuje jakąś pojedynczą listę - i powinna, jako odpowiednia lista, zaimplementować 'IList '. Oczywiście, obejście problemu z instancją klasy implementującej 'IList ' w klasie statycznej jest wykonalne (i jestem świadomy, że C# obecnie na razie pozwala to tylko w ten sposób), ale możliwość bezpośredniego deklarowania klasy jako statycznie implementacja interfejsu usunie niepotrzebny poziom pośrednictwa. –

Odpowiedz

28

Nie, obsługa języka nie jest dostępna w języku C#. Są dwa obejścia, które mogę natychmiast rozważyć:

  • używać refleksji w czasie wykonywania; skrzyżowane palce i nadzieja ...
  • używają singleton/default instancji/podobną zaimplementować interfejs, który deklaruje Metody

(aktualizacji)

Rzeczywiście, tak długo, jak masz jednostkę - testowanie, pierwsza opcja nie jest tak źle, jak mogłoby się wydawać, jeśli (tak jak ja) pochodzisz ze ścisłego tła "statycznego pisania". Faktem jest; działa dobrze w językach dynamicznych. I rzeczywiście, tak właśnie działa mój kod generic operators - ma on operatorów statycznych. W czasie działania, jeśli tego nie zrobisz, będzie się z ciebie śmiać w odpowiednio kpiącym tonie ... ale nie może sprawdzić podczas kompilacji.

+11

Chciałbym, jeśli kod roześmiałby się na mnie w odpowiednim kpiącym tonie. Zaczekaj, to się stanie przez cały czas. Nieważne. ;-) –

4

Niestety, nie, nic takiego nie jest wbudowane w ten język.

0

Nie, nie byłoby sensu w tej funkcji. Interfejsy są zasadniczo skalowaną formą dziedziczenia wielokrotnego. Informują kompilator, jak skonfigurować wirtualną tablicę funkcji, aby niemające właściwości statycznych metody wirtualne mogły być poprawnie wywoływane w klasach potomnych. Metody statyczne nie mogą być wirtualne, dlatego nie ma sensu ich używać.

+2

Zdecydowanie * jest * punktem w tym - tylko dlatego, że metody nie będą wywoływane przez interfejs, nie oznacza to, że nie warto testować ich obecności. To trochę jak nowe ograniczenie T(), które C# obnaża - faktyczna implementacja nowej T() skutecznie przechodzi przez refleksję ... –

+0

Nie chcę, żeby były wirtualne, po prostu zmuszają je do istnienia. – tpower

+0

Jestem z dsimcha na ten temat. Jeśli celem jawnie zadeklarowanego interfejsu nie jest oddzielenie interfejsu od implementacji, to pozostało niewiele "interfejsu". Jeśli potrzebujesz, aby niektóre klasy miały pewne metody, to możesz wymyślić jakiś inny element języka, np. HasToImplementMethodsAttrib – Dan

15

Nie. Zasadniczo brzmi to jakbyś był w rodzaju "statycznego polimorfizmu". To nie istnieje w C#, chociaż zasugerowałem rodzaj "static interface" notion which could be useful in terms of generics.

Jedną z rzeczy, które możesz może zrobić, to napisać prosty test jednostkowy, aby sprawdzić, czy wszystkie typy w danym zespole przestrzegają twoich zasad. Jeśli inni deweloperzy będą również implementować interfejs, możesz umieścić ten kod testowy w jakimś popularnym miejscu, aby wszyscy implementujący interfejs mogli łatwo przetestować własne złożenia.

+0

W umowie z interfejsem można określić, że wszystkie * legalne * implementacje interfejsu muszą mieć określonych członków statycznych * i *, jeśli są otwierane, zawierają w swojej umowie dziedziczenia wymóg, aby wszystkie * legalne * wyprowadzone typy musiały spełniać te same wymagania. To nie powstrzymałoby ludzi przed pisaniem nielegalnych implementacji interfejsu, ale sytuacja nie byłaby gorsza niż w przypadku prawie każdego innego interfejsu. – supercat

+0

@supercat: Przez "kontrakt" rozumiesz tylko "dokumentację" lub coś w rodzaju kontraktów kodu? –

+0

Miałem na myśli dokumentację. Każdy znaczący interfejs musi mieć zestaw wymagań, tak aby implementacje spełniające te wymagania były zgodne z prawem, a implementacje niespełniające tych wymagań są bezprawne. Nie ma ustalonego formatu, w którym takie wymagania muszą być wyrażone, ale jeśli np. implementacja 'IList ' miała ustawnik właściwości, który zignorował parametr 'index' i po prostu zastąpił element losowy, wiele metod, które spodziewają się pracować z' IList ', mogłoby zawieść, nie dlatego, że metody były wadliwe, ale ponieważ 'IList ' Implementacja była bezprawna. – supercat

3

Chociaż nie ma wsparcia językowego dla tego, można użyć narzędzia do analizy statycznej, aby go wymusić. Na przykład możesz napisać niestandardową regułę dla FxCop, która wykrywa atrybut lub implementację interfejsu dla klasy, a następnie sprawdza istnienie pewnych metod statycznych.

0

Podejście, które przybliża cię do tego, czego potrzebujesz, to singleton, jak sugerował Marc Gravell.

Interfejsy, między innymi, pozwalają zapewnić pewien poziom abstrakcji dla klas, dzięki czemu można korzystać z danego interfejsu API niezależnie od typu, który je implementuje. Ponieważ jednak musisz znać typ klasy statycznej, aby z niej skorzystać, dlaczego chcesz wymusić na tej klasie implementację zestawu funkcji?

Może mógłbyś użyć niestandardowego atrybutu takiego jak [ImplementsXXXInterface] i zapewnić pewne sprawdzanie czasu wykonywania, aby upewnić się, że klasy z tym atrybutem faktycznie implementują interfejs, którego potrzebujesz?

+0

Sprawdzanie czasu wykonywania nie jest dobre, ponieważ zostanie ono już pobrane przez kompilator podczas budowania kodu konsumenta. Zastanawiam się, czy mógłbym uzyskać kompilator, aby go wcześniej odebrać. – tpower

1

Wzorzec singletonowy nie pomaga we wszystkich przypadkach. Mój przykład pochodzi z mojego prawdziwego projektu. Nie jest wymyślone.

Mam klasę (nazwijmy ją "Widget"), która dziedziczy z klasy w ORM innej firmy. Jeśli utworzę obiekt widgetu (tworząc w ten sposób wiersz w db) tylko po to, aby upewnić się, że moje metody statyczne są zadeklarowane, robię większy bałagan niż ten, który próbuję posprzątać.

Jeśli utworzyć ten dodatkowy obiekt w magazynie danych, mam go ukryć przed użytkownikami, obliczenia itp

używać interfejsów w języku C#, aby upewnić się, że wdrożenie wspólnych cech w zestawie klasy.

Niektóre metody implementujące te funkcje wymagają uruchomienia danych instancji. Koduję te metody jako metody instancji i używam interfejsu C#, aby upewnić się, że istnieją w klasie.

Niektóre z tych metod nie wymagają danych instancji, więc są to metody statyczne. Gdybym mógł zadeklarować interfejsy za pomocą metod statycznych, kompilator mógłby sprawdzić, czy te metody istnieją w klasie, która mówi, że implementuje interfejs.

5

To jest świetne pytanie, które napotkałem w moich projektach.

Niektórzy ludzie utrzymują, że interfejsy i klasy abstrakcyjne istnieją tylko dla polimorfizmu, a nie dla forsowania typów do implementacji określonych metod. Osobiście uważam polimorfizm za podstawowy przypadek użycia, a przymusowa implementacja za wtórną. Często stosuję technikę wymuszonej implementacji. Zazwyczaj pojawia się w kodzie kodu implementującego wzorzec szablonu. Klasa bazowa/szablon zawiera pewną złożoną ideę, a podklasy zapewniają liczne warianty, stosując metody abstrakcyjne. Jedną z pragmatycznych korzyści jest to, że abstrakcyjne metody dostarczają wskazówek innym programistom wdrażającym podklasy. Program Visual Studio ma nawet możliwość wypróbowania metod. Jest to szczególnie przydatne, gdy deweloper musi dodać nową podklasę kilka miesięcy lub lat później.

Wadą jest brak konkretnego wsparcia dla niektórych z tych scenariuszy szablonów w języku C#. Metody statyczne to jedność. Kolejny to konstruktorzy; Idealnie ISerializable powinno zmusić programistę do wdrożenia chronionego konstruktora serializacji.

Najprostszym podejściem jest prawdopodobnie (zgodnie z sugestią wcześniejszą) skorzystanie z automatycznego testu w celu sprawdzenia, czy metoda statyczna jest zaimplementowana na pożądanych typach. Innym dobrym pomysłem, o którym już wspomniano, jest implementacja reguły analizy statycznej.

Trzecią opcją jest użycie architektury programowania zorientowanej na aspekt, takiej jak PostSharp. PostSharp obsługuje sprawdzanie poprawności aspektów w czasie kompilacji. Możesz napisać kod .NET, który odbija się w zespole podczas kompilacji, generując dowolne ostrzeżenia i błędy. Zwykle robi się to, aby sprawdzić, czy użycie aspektu jest odpowiednie, ale nie rozumiem, dlaczego nie można go użyć do sprawdzania poprawności reguł szablonów.

0

Jeśli dopiero po otrzymaniu tych błędów kompilatora, należy rozważyć tę konfigurację:

  1. zdefiniować metody w interfejsie.
  2. Deklaracja metod za pomocą streszczenia.
  3. Zaimplementuj publiczne metody statyczne i zastosuj abstrakcyjne metody nadpisania, po prostu wywołaj metody statyczne.

To trochę dodatkowego kodu, ale będziesz wiedzieć, kiedy ktoś nie stosuje wymaganej metody.

Powiązane problemy