Dlaczego C # został zaprojektowany w ten sposób?
Jak rozumiem, interfejs opisuje tylko zachowanie i służy opisaniu zobowiązania umownego dla klas wdrażających interfejs, że określone zachowanie jest realizowane.
Jeśli klasy chcą zaimplementować to zachowanie we wspólnej metodzie, dlaczego nie powinny?
Oto przykład tego, co mam na myśli:
// These items will be displayed in a list on the screen.
public interface IListItem {
string ScreenName();
...
}
public class Animal: IListItem {
// All animals will be called "Animal".
public static string ScreenName() {
return "Animal";
}
....
}
public class Person: IListItem {
private string name;
// All persons will be called by their individual names.
public string ScreenName() {
return name;
}
....
}
c#
oop
language-features
Kramii
źródło
źródło
IListItem.ScreenName() => ScreenName()
(przy użyciu składni C # 7) jawnie zaimplementuje metodę interfejsu, wywołując metodę statyczną. Kiedy dodaje się do tego dziedziczenie, robi się brzydko (trzeba ponownie zaimplementować interfejs)Odpowiedzi:
Zakładając, że pytasz, dlaczego nie możesz tego zrobić:
To nie ma dla mnie sensu semantycznie. Powinny istnieć metody określone w interfejsie w celu określenia umowy dotyczącej interakcji z obiektem. Metody statyczne nie pozwalają na interakcję z obiektem - jeśli znajdziesz się w pozycji, w której twoja implementacja może stać się statyczna, być może będziesz musiał zadać sobie pytanie, czy ta metoda naprawdę należy do interfejsu.
Aby zaimplementować twój przykład, nadałbym zwierzęciu właściwość const, która nadal umożliwiałaby dostęp do niego ze statycznego kontekstu i zwróciłaby tę wartość w implementacji.
W przypadku bardziej skomplikowanej sytuacji zawsze możesz zadeklarować inną metodę statyczną i delegować ją. Próbując wymyślić przykład, nie mogłem wymyślić żadnego powodu, dla którego zrobiłbyś coś nietrywialnego zarówno w kontekście statycznym, jak i instancji, więc oszczędzę ci obiektu blob FooBar i wezmę to za wskazówkę, że może nie być dobrym pomysłem.
źródło
public static object MethodName(this IBaseClass base)
w klasie statycznej. Minusem jest jednak to, że w przeciwieństwie do dziedziczenia interfejsu - nie wymusza to / nie pozwala poszczególnym spadkobiercom na dobre zastąpienie metodologii.Moim (uproszczonym) technicznym powodem jest to, że metod statycznych nie ma w tabeli vt , a strona połączenia jest wybierana w czasie kompilacji. Jest to ten sam powód, dla którego nie można zastąpić ani wirtualnego elementu statycznego. Aby uzyskać więcej informacji, potrzebujesz pakietu CS grad lub kompilatora - z których ja też nie jestem.
Ze względów politycznych zacytuję Erica Lipperta (który jest winkiem kompilatora i posiada licencjat z matematyki, informatyki i matematyki stosowanej z University of Waterloo (źródło: LinkedIn ):
Zauważ, że Lippert nie pozostawia miejsca na tak zwaną metodę typu:
ale trzeba się jeszcze przekonać o jego przydatności.
źródło
object
s i jak mogą implementować interfejsy.Większość odpowiedzi tutaj wydaje się pomijać cały punkt. Polimorfizm można stosować nie tylko między instancjami, ale także między typami. Jest to często potrzebne, gdy używamy leków generycznych.
Załóżmy, że mamy parametr typu w metodzie ogólnej i musimy wykonać z nim pewną operację. Nie chcemy tworzyć instancji, ponieważ nie jesteśmy świadomi konstruktorów.
Na przykład:
Niestety mogę wymyślić tylko „brzydkie” alternatywy:
Użyj refleksji Ugly i przebija ideę interfejsów i polimorfizmu.
Utwórz całkowicie oddzielną klasę fabryczną
Może to znacznie zwiększyć złożoność kodu. Na przykład, jeśli próbujemy modelować obiekty domeny, każdy obiekt potrzebuje innej klasy repozytorium.
Utwórz instancję, a następnie wywołaj żądaną metodę interfejsu
Może to być trudne do wdrożenia, nawet jeśli kontrolujemy źródło klas, używane jako parametry ogólne. Powodem jest to, że na przykład możemy potrzebować, aby instancje były tylko w dobrze znanym stanie „podłączony do bazy danych”.
Przykład:
aby użyć rozwiązania do rozwiązywania problemu statycznego interfejsu, musimy wykonać następujące czynności:
Jest to oczywiście brzydkie, a także niepotrzebne komplikuje kod dla wszystkich innych metod. Oczywiście nie jest to również eleganckie rozwiązanie!
źródło
public abstract class DBObject<T> where T : DBObject<T>, new()
a następnie sprawi, że wszystkie klasy DB odziedziczą jakoDBObject<T>
. Następnie możesz ustawić funkcję pobierania według klucza jako funkcję statyczną z typem zwracanym T w abstrakcyjnej nadklasie, zmusić tę funkcję do utworzenia nowego obiektu T, a następnie wywołać chronionyString GetRetrieveByKeyQuery()
(zdefiniowany jako abstrakcyjny w nadklasie) na tym obiekcie, aby uzyskać rzeczywiste zapytanie do wykonania. Chociaż może to być odrywane od tematuWiem, że to stare pytanie, ale interesujące. Przykład nie jest najlepszy. Myślę, że byłoby znacznie jaśniej, gdybyś pokazał przypadek użycia:
Zwykłe posiadanie statycznych metod implementujących interfejs nie osiągnęłoby tego, co chcesz; potrzebne byłyby elementy statyczne jako część interfejsu. Z pewnością mogę sobie wyobrazić wiele przypadków użycia, szczególnie jeśli chodzi o możliwość tworzenia rzeczy. Dwa podejścia, które mogę zaoferować, które mogą być pomocne:
Żadne z tych podejść nie jest tak naprawdę atrakcyjne. Z drugiej strony spodziewałbym się, że gdyby mechanizmy istniały w CLR w celu zapewnienia tego rodzaju funkcjonalności w sposób czysty, .net pozwoliłby na określenie sparametryzowanych „nowych” ograniczeń (ponieważ poznanie, czy klasa ma konstruktora z określoną sygnaturą, wydaje się trudności w porównywaniu z wiedzą, czy ma metodę statyczną z określonym podpisem).
źródło
Krótkowzroczność, tak sądzę.
Pierwotnie zaprojektowane interfejsy miały być używane tylko z instancjami klasy
Dopiero wraz z wprowadzeniem interfejsów, ponieważ ograniczenia generyczne rzeczywiście dodały statyczną metodę do interfejsu, miały praktyczne zastosowanie.
(odpowiadając na komentarz :) Wierzę, że zmiana go teraz wymagałaby zmiany CLR, co doprowadziłoby do niezgodności z istniejącymi zestawami.
źródło
Interfejsy określają zachowanie obiektu.
Metody statyczne nie określają zachowania obiektu, ale zachowanie, które w jakiś sposób wpływa na obiekt.
źródło
W zakresie, w jakim interfejsy stanowią „kontrakty”, wydaje się, że klasy statyczne mogą implementować interfejsy.
Powyższe argumenty wydają się pomijać ten punkt dotyczący umów.
źródło
Ponieważ celem interfejsu jest umożliwienie polimorfizmu, możliwość przekazania instancji dowolnej liczby zdefiniowanych klas, które wszystkie zostały zdefiniowane, w celu implementacji zdefiniowanego interfejsu ... gwarantując, że w twoim wywołaniu polimorficznym kod będzie w stanie znaleźć metoda, którą wołasz. nie ma sensu zezwalać na statyczną metodę implementacji interfejsu,
Jak byś to nazwał?
źródło
Whatever<T>() where T:IMyStaticInterface
, mógłbym zadzwonićWhatever<MyClass>()
i miećT.MyStaticMethod()
wewnątrzWhatever<T>()
implementacji bez potrzeby wystąpienia. Metoda wywołania zostanie określona w czasie wykonywania. Możesz to zrobić poprzez refleksję, ale nie ma „umowy”, którą trzeba by wymusić.Jeśli chodzi o metody statyczne stosowane w kontekstach innych niż ogólne, zgadzam się, że nie ma sensu dopuszczać ich w interfejsach, ponieważ nie byłoby możliwe ich wywołanie, gdybyś miał odniesienie do interfejsu. Istnieje jednak fundamentalna dziura w projektowaniu języka utworzona przy użyciu interfejsów NIE w kontekście polimorficznym, ale w ogólnym. W tym przypadku interfejs wcale nie jest interfejsem, ale raczej ograniczeniem. Ponieważ C # nie ma pojęcia ograniczenia poza interfejsem, brakuje znacznej funkcjonalności. Przykładem:
Tutaj nie ma polimorfizmu, rodzajowy używa rzeczywistego typu obiektu i wywołuje operator + =, ale to się nie udaje, ponieważ nie jest pewne, czy ten operator istnieje. Najprostszym rozwiązaniem jest określenie go w ograniczeniu; proste rozwiązanie jest niemożliwe, ponieważ operatory są statyczne, a metody statyczne nie mogą znajdować się w interfejsie, a (tutaj jest problem) ograniczenia są przedstawiane jako interfejsy.
To, czego potrzebuje C #, to prawdziwy typ ograniczenia, wszystkie interfejsy byłyby również ograniczeniami, ale nie wszystkie ograniczenia byłyby interfejsami, to możesz to zrobić:
Wiele mówi się już o tworzeniu IArithmetic dla wszystkich typów liczbowych do implementacji, ale istnieje obawa o wydajność, ponieważ ograniczenie nie jest konstrukcją polimorficzną, wprowadzenie ograniczenia CArithmetic rozwiązałoby ten problem.
źródło
T
powinien mieć statyczny członek (np. fabryka produkującaT
instancje). Myślę, że nawet bez zmian w środowisku wykonawczym byłoby możliwe zdefiniowanie konwencji i metod pomocniczych, które pozwoliłyby językom na wdrożenie czegoś takiego jak cukier syntaktyczny w efektywny i interoperacyjny sposób. Jeśli dla każdego interfejsu z wirtualnymi metodami statycznymi istniała klasa pomocnicza ...Ponieważ interfejsy mają strukturę dziedziczenia, a metody statyczne nie dziedziczą dobrze.
źródło
Wydaje się, że chcesz, aby metoda statyczna była wywoływana zarówno przez typ, jak i dowolną instancję tego typu. Doprowadziłoby to przynajmniej do dwuznaczności, która nie jest pożądaną cechą.
Odbywałyby się niekończące się debaty na temat tego, czy ma to znaczenie, co jest najlepszą praktyką i czy występują problemy z wydajnością w ten czy inny sposób. Po prostu nie obsługując tego C # oszczędza nam martwienia się o to.
Jest również prawdopodobne, że kompilator, który spełniłby to pragnienie, utraciłby pewne optymalizacje, które mogą wynikać z bardziej ścisłego oddzielenia metod instancji od metod statycznych.
źródło
Można myśleć o metodach statycznych i metodach niestatycznych klasy jako o różnych interfejsach. Po wywołaniu metody statyczne są tłumaczone na obiekt klasy statycznej singleton, a metody niestatyczne na instancję klasy, z którą mamy do czynienia. Tak więc, jeśli użyjesz statycznych i niestatycznych metod w interfejsie, efektywnie zadeklarujesz dwa interfejsy, kiedy naprawdę chcemy, aby interfejsy były używane do uzyskania dostępu do jednej spójnej rzeczy.
źródło
Aby podać przykład, w którym brakuje mi albo statycznej implementacji metod interfejsu, albo tego, co Mark Brackett wprowadził jako „tak zwaną metodę typu”:
Podczas odczytu z pamięci bazy danych mamy ogólną klasę DataTable, która obsługuje odczytywanie z tabeli o dowolnej strukturze. Wszystkie informacje specyficzne dla tabeli są umieszczane w jednej klasie na tabelę, która zawiera również dane dla jednego wiersza z bazy danych i która musi implementować interfejs IDataRow. W IDataRow zawarty jest opis struktury tabeli do odczytu z bazy danych. DataTable musi zapytać o strukturę danych z IDataRow przed odczytem z DB. Obecnie wygląda to tak:
GetDataStructure jest wymagany tylko raz dla każdej tabeli do odczytu, narzut związany z tworzeniem jednej kolejnej instancji jest minimalny. Jednak w tym przypadku byłoby miło.
źródło
FYI: Możesz uzyskać podobne zachowanie do tego, co chcesz, tworząc metody rozszerzenia interfejsu. Metodą rozszerzenia byłoby wspólne, nieprzekraczalne zachowanie statyczne. Niestety, ta metoda statyczna nie byłaby częścią umowy.
źródło
Interfejsy to abstrakcyjne zestawy zdefiniowanych dostępnych funkcji.
To, czy metoda w tym interfejsie zachowuje się jako statyczna, czy nie, jest szczegółem implementacji, który powinien być ukryty za interfejsem . Błędem byłoby zdefiniowanie metody interfejsu jako statycznej, ponieważ niepotrzebnie zmuszałbyś metodę do implementacji w określony sposób.
Gdyby metody zostały zdefiniowane jako statyczne, klasa implementująca interfejs nie byłaby tak enkapsulowana, jak mogłaby być. Enkapsulacja jest dobrą rzeczą, do której należy dążyć w projektowaniu obiektowym (nie będę się zastanawiać, dlaczego, możesz przeczytać to tutaj: http://en.wikipedia.org/wiki/Object-oriented ). Z tego powodu metody statyczne nie są dozwolone w interfejsach.
źródło
Klasy statyczne powinny być w stanie to zrobić, aby mogły być używane ogólnie. Zamiast tego musiałem wdrożyć Singleton, aby osiągnąć pożądane rezultaty.
Miałem kilka klas statycznych warstw biznesowych, w których zaimplementowano metody CRUD, takie jak „Utwórz”, „Przeczytaj”, „Aktualizuj”, „Usuń” dla każdego typu jednostki, np. „Użytkownik”, „Zespół” itp. Następnie utworzyłem bazę kontrola, która miała właściwość abstrakcyjną dla klasy warstwy biznesowej, która implementowała metody CRUD. Umożliwiło mi to zautomatyzowanie operacji „Utwórz”, „Przeczytaj”, „Aktualizuj”, „Usuń” z klasy podstawowej. Musiałem użyć Singletona z powodu ograniczeń statycznych.
źródło
Wydaje się, że większość ludzi zapomina, że w klasach OOP są również obiekty, a więc mają wiadomości, które z jakiegoś powodu c # nazywają „metodą statyczną”. Fakt, że istnieją różnice między obiektami instancji a obiektami klasy, pokazuje tylko wady lub wady języka. Optymista o c # chociaż ...
źródło
OK, oto przykład potrzeby użycia „metody typu”. Tworzę jeden z zestawu klas opartych na źródłowym pliku XML. Więc mam
funkcja wywoływana z kolei w każdej klasie.
Funkcja powinna być statyczna, ponieważ w przeciwnym razie tracimy czas na tworzenie nieodpowiednich obiektów. Jak zauważa @Ian Boyde, można tego dokonać w klasie fabrycznej, ale to tylko zwiększa złożoność.
Byłoby miło dodać go do interfejsu, aby zmusić implementatory klasy do jego wdrożenia. Nie spowodowałoby to znacznego obciążenia - to tylko kontrola czasu kompilacji / łącza i nie wpływa na vtable.
Byłaby to jednak dość niewielka poprawa. Ponieważ metoda jest statyczna, ja jako osoba wywołująca muszę ją jawnie wywołać, więc otrzymam natychmiastowy błąd kompilacji, jeśli nie zostanie zaimplementowany. Zezwolenie na określenie go w interfejsie oznaczałoby, że ten błąd pojawia się nieznacznie wcześniej w cyklu programowania, ale jest to trywialne w porównaniu z innymi problemami z uszkodzonym interfejsem.
Jest to więc niewielka potencjalna cecha, która prawdopodobnie jest najlepiej pominięta.
źródło
Fakt, że klasa statyczna jest zaimplementowana w języku C # przez Microsoft, tworząc specjalną instancję klasy z elementami statycznymi, jest po prostu osobliwością w jaki sposób osiąga się funkcjonalność statyczną. To nie jest teoretyczny punkt.
Interfejs MUSI być deskryptorem interfejsu klasy - lub jego interakcji, i powinien obejmować interakcje, które są statyczne. Ogólna definicja interfejsu (z Meriam-Webster): miejsce lub obszar, w którym różne rzeczy spotykają się i komunikują ze sobą lub wpływają na siebie. Kiedy całkowicie pomijasz elementy statyczne klasy lub klasy statyczne, ignorujemy duże sekcje interakcji tych złych chłopców.
Oto bardzo wyraźny przykład, w którym użyteczne byłoby używanie interfejsów z klasami statycznymi:
Obecnie piszę klasy statyczne zawierające te metody bez żadnego sprawdzania, aby upewnić się, że niczego nie zapomniałem. To jak złe czasy programowania przed OOP.
źródło
C # i CLR powinny obsługiwać metody statyczne w interfejsach, podobnie jak Java. Modyfikator statyczny jest częścią definicji kontraktu i ma znaczenie, w szczególności, że zachowanie i wartość zwracana nie różnią się w zależności od instancji, chociaż może się różnić w zależności od połączenia.
To powiedziawszy, polecam, jeśli chcesz użyć metody statycznej w interfejsie i nie możesz, zamiast tego użyj adnotacji. Otrzymasz funkcjonalność, której szukasz.
źródło
Myślę, że krótka odpowiedź brzmi „ponieważ ma zerową przydatność”. Aby wywołać metodę interfejsu, potrzebujesz instancji typu. Z metod instancji można wywoływać dowolne metody statyczne.
źródło
Myślę, że pytanie polega na tym, że C # potrzebuje innego słowa kluczowego, właśnie w takiej sytuacji. Chcesz metody, której zwracana wartość zależy tylko od typu, na który jest wywoływana. Nie można nazwać go „statycznym”, jeśli wspomniany typ jest nieznany. Ale gdy typ zostanie poznany, stanie się statyczny. „Nierozstrzygnięty statyczny” to pomysł - jeszcze nie jest statyczny, ale kiedy poznamy typ odbiornika, będzie. Jest to idealna koncepcja, dlatego programiści wciąż o nią proszą. Nie pasowało to jednak do sposobu, w jaki projektanci myśleli o języku.
Ponieważ nie jest dostępny, zacząłem używać metod niestatycznych w sposób pokazany poniżej. Niezupełnie idealne, ale nie widzę żadnego podejścia, które miałoby większy sens, przynajmniej nie dla mnie.
źródło
Jeśli więc chcesz uzyskać dostęp do metod umowy o interfejs, musisz utworzyć obiekt. Zawsze jest to niedozwolone w przypadku metod statycznych. Klasy statyczne, metoda i zmienne nigdy nie wymagają obiektów i ładowania do pamięci bez tworzenia obiektu tego obszaru (lub klasy), lub można powiedzieć, że nie wymagają tworzenia obiektu.
źródło
Koncepcyjnie nie ma powodu, dla którego interfejs nie mógłby zdefiniować kontraktu zawierającego metody statyczne.
W przypadku bieżącej implementacji języka C # ograniczenie wynika z możliwości dziedziczenia klasy podstawowej i interfejsów. Jeśli „klasa SomeBaseClass” zaimplementuje „interfejs ISomeInterface” i „klasa SomeDerivedClass: SomeBaseClass, ISomeInterface” również zaimplementuje interfejs, metoda statyczna w celu zaimplementowania metody interfejsu zakończy się niepowodzeniem, ponieważ metoda statyczna nie może mieć takiej samej sygnatury jak metoda instancji (co być obecnym w klasie podstawowej w celu wdrożenia interfejsu).
Klasa statyczna jest funkcjonalnie identyczna z singletonem i służy temu samemu celowi, co singleton z czystszą składnią. Ponieważ singleton może implementować interfejs, implementacje interfejsu według statyki są poprawne koncepcyjnie.
Sprowadza się to po prostu do ograniczenia na przykład konfliktu nazw C # i metod statycznych o tej samej nazwie w ramach dziedziczenia. Nie ma powodu, dla którego język C # nie mógłby zostać „zaktualizowany” do obsługi statycznych kontraktów metod (interfejsów).
źródło
Kiedy klasa implementuje interfejs, tworzy instancję dla elementów interfejsu. Chociaż typ statyczny nie ma instancji, nie ma sensu mieć podpisów statycznych w interfejsie.
źródło