Jest dobrze znane osobom zaznajomionym z historią, że C # i środowisko .NET powstały jako „Delphi przepisane, aby poczuć się jak Java”, zaprojektowane przez głównego programistę Delphi, Andersa Hejlsberga. Od tamtej pory sytuacja się nieco rozbiegła, ale na początku podobieństwa były tak oczywiste, że pojawiły się nawet poważne spekulacje, że .NET był pierwotnie produktem Borlanda.
Ale ostatnio przyglądałem się niektórym rzeczom .NET i wydaje się, że brakuje jednej z najbardziej interesujących i użytecznych funkcji Delphi: koncepcji klas jako pierwszorzędnego typu danych. Dla tych, którzy go nie znają, typ TClass
reprezentuje odwołanie do klasy, podobnie jak Type
typ w .NET. Ale tam, gdzie .NET używa Type
do refleksji, Delphi wykorzystuje TClass
jako bardzo ważną wbudowaną część języka. Pozwala na różne przydatne idiomy, które po prostu nie istnieją i nie mogą bez niego istnieć, takie jak zmienne podtypu klasy i metody klas wirtualnych.
Każdy język OO ma wirtualne metody, w których różne klasy wdrażają tę samą podstawową koncepcję metody na różne sposoby, a następnie właściwa metoda jest wywoływana w czasie wykonywania na podstawie rzeczywistego typu instancji obiektu, w której jest wywoływana. Delphi rozszerza tę koncepcję na klasy: jeśli masz odwołanie TClass zdefiniowane jako określony podtyp klasy (tj. class of TMyClass
Oznacza, że zmienna może akceptować dowolne odwołanie do klasy, które dziedziczy TMyClass
, ale nic poza dziedziczeniem ), do którego dołączone są wirtualne metody klasy można je wywoływać bez instancji, używając rzeczywistego typu klasy. Zastosowanie tego wzorca do konstruktorów sprawia, że implementacja fabryki jest na przykład trywialna.
Wydaje się, że w .NET nie ma nic równoważnego. Czy są tak przydatne, jak odniesienia klas (a zwłaszcza wirtualne konstruktory i inne wirtualne metody klas!), Czy ktoś powiedział coś o tym, dlaczego zostały pominięte?
Konkretne przykłady
Formularz Deserializacja
Delphi VCL zapisuje formularze w DFM
formacie DSL do opisu hierarchii komponentów. Kiedy czytnik formularzy analizuje dane DFM, działa na obiektach opisanych w następujący sposób:
object Name: ClassName
property = value
property = value
...
object SubObjectName: ClassName
...
end
end
Interesująca jest tutaj ClassName
część. Każda klasa komponentów rejestruje TClass
ją jednocześnie w systemie przesyłania strumieniowego komponentów initialization
(pomyśl, że statyczne konstruktory, tylko nieco inne, gwarantują, że wystąpią natychmiast po uruchomieniu). To rejestruje każdą klasę w haszu-ciągu TClass z nazwą klasy jako kluczem.
Każdy element pochodzi od TComponent
, który ma wirtualny konstruktor, który pobiera jeden argument Owner: TComponent
. Każdy komponent może zastąpić tego konstruktora, aby zapewnić własną inicjalizację. Gdy czytnik DFM odczytuje nazwę klasy, wyszukuje nazwę we wspomnianym haszapie i pobiera odpowiednie odwołanie do klasy (lub podnosi wyjątek, jeśli go nie ma), a następnie wywołuje na nim wirtualnego konstruktora TComponent, o którym wiadomo, że jest dobry ponieważ funkcja rejestracji pobiera odwołanie do klasy, która jest wymagana do zejścia z TComponent, i otrzymujesz obiekt odpowiedniego typu.
Brakuje tego, odpowiednik WinForms jest ... cóż ... wielki bałagan, mówiąc wprost, wymagając nowego języka .NET, aby całkowicie zaimplementować własną formę (de) serializacji. To trochę szokujące, kiedy się nad tym zastanowić; ponieważ celem CLR jest umożliwienie wielu językom korzystania z tej samej podstawowej infrastruktury, system w stylu DFM miałby doskonały sens.
Rozciągliwość
Klasa menedżera obrazów, którą napisałem, może zostać wyposażona w źródło danych (takie jak ścieżka do plików obrazów), a następnie automatycznie ładować nowe obiekty obrazów, jeśli spróbujesz odzyskać nazwę, której nie ma w kolekcji, ale jest ona dostępna w źródle danych. Ma zmienną klasy wpisaną jako class of
podstawowa klasa obrazu, reprezentująca klasę wszelkich nowych obiektów, które mają zostać utworzone. Jest dostarczany z ustawieniem domyślnym, ale przy tworzeniu nowych obrazów o specjalnych celach istnieją pewne punkty, że obrazy należy konfigurować na różne sposoby. (Tworzenie go bez kanału alfa, pobieranie specjalnych metadanych z pliku PNG w celu określenia rozmiaru duszka itp.)
Można to zrobić, pisząc duże ilości kodu konfiguracyjnego i przekazując specjalne opcje do wszystkich metod, które mogą w końcu stworzyć nowy obiekt ... lub możesz po prostu stworzyć podklasę podstawowej klasy obrazu, która zastępuje metodę wirtualną, w której dany aspekt zostanie skonfigurowany, a następnie użyj bloku try / last, aby tymczasowo zastąpić właściwość „domyślna klasa” w razie potrzeby, a następnie przywrócić ją. Robienie tego przy pomocy zmiennych referencyjnych klas jest znacznie prostsze i nie jest czymś, co można by zrobić za pomocą generyków.
TClass
użyteczne jest, wraz z przykładowym kodem? W moich pobieżnych poszukiwaniach Internetu TClass
stwierdzam, że TClass
można to przekazać jako parametr. Odbywa się to w .NET przy użyciu Generics. Metody fabryczne są po prostu oznaczone static
w .NET i nie wymagają wykonania instancji klasy.
TClass
to podstawowa funkcja językowa, która wymaga obsługi kompilatora. Nie można „pisać własnego” bez pisania własnego języka, a nawet dla platformy .NET nie byłoby to wystarczające, ponieważ model obiektowy jest definiowany przez CLR, a nie przez poszczególne języki. Jest to coś, co dosłownie musi być częścią samego środowiska .NET, albo nie może istnieć.