Dlaczego .NET Framework nie ma pojęcia klas jako typów pierwszej klasy?

20

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 TClassreprezentuje odwołanie do klasy, podobnie jak Typetyp w .NET. Ale tam, gdzie .NET używa Typedo refleksji, Delphi wykorzystuje TClassjako 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 TMyClassOznacza, ż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 DFMformacie 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 ClassNameczęść. Każda klasa komponentów rejestruje TClassją 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 ofpodstawowa 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.

Mason Wheeler
źródło
3
Czy możesz podać jeden lub dwa konkretne przykłady, w których TClassużyteczne jest, wraz z przykładowym kodem? W moich pobieżnych poszukiwaniach Internetu TClassstwierdzam, że TClassmożna to przekazać jako parametr. Odbywa się to w .NET przy użyciu Generics. Metody fabryczne są po prostu oznaczone staticw .NET i nie wymagają wykonania instancji klasy.
Robert Harvey
7
@RobertHarvey: Dla mnie osobiście C # zaczął mieć o wiele więcej sensu, kiedy przestałem patrzeć na to, jak Java / C ++ przeładowuje i zacząłem traktować go jak Modula-2 z obiektami. Tak samo jest z Javą: wszyscy mówią, że był pod wpływem C ++, ale to nieprawda. Jego główny wpływ to Objective-C (a przez to Smalltalk), a Java nabierze o wiele więcej sensu, gdy potraktujesz go jako Smalltalk z typami zamiast C ++ z GC.
Jörg W Mittag,
3
@RobertHarvey: TClassto 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ć.
Mason Wheeler,
4
Przede wszystkim mogę z całkowitą pewnością stwierdzić, że .NET nie był pierwotnie produktem „Borland”. Skąd to wiem? Byłem (nadal jestem) częścią zespołu podstawowego, który opracował Delphi. Ściśle współpracowałem z Anders, Chuck, Gary i innymi. Oczywiście jestem pewien, że o tym wiesz. Jeśli chodzi o istnienie odwołania do klasy (jak nazywane są TClass i podobne konstrukty) w .NET prawdopodobnie został uznany za niepotrzebny ze względu na istnienie bogatej informacji o typie dostępnym w czasie wykonywania. Delphi na początku posiadało znacznie mniej informacji o typie, a referencje klasowe były kompromisem.
Allen Bauer,

Odpowiedzi:

5

.NET (CLR) to trzecia generacja modelu Component Object Model (COM) firmy Microsoft, który na początku był nazywany „środowiskiem uruchomieniowym COM +”. Microsoft Visual Basic i rynek kontrolek COM / ActiveX miał znacznie większy wpływ na wybrane opcje kompatybilności architektury CLR niż Borland Delphi. (Wprawdzie fakt, że Delphi zastosował formanty ActiveX, z pewnością pomógł rozwinąć ekosystem ActiveX, ale COM / ActiveX istniało przed Delphi)

Architektura COM została opracowana w C (nie w C ++) i skoncentrowana na interfejsach, a nie klasach. Ponadto obsługiwana kompozycja obiektów, co oznacza, że ​​obiekt COM może faktycznie składać się z kilku różnych obiektów, a interfejs IUnknown łączy je ze sobą. Ale IUnknown nie odegrał żadnej roli w tworzeniu obiektów COM, które zostały zaprojektowane tak, aby były jak najbardziej niezależne od języka. Tworzenie obiektów było ogólnie obsługiwane przez IClassFactory, a odbicie przez ITypeLibrary i powiązane interfejsy. Ten podział problemów był niezależny od języka implementacji, a funkcje każdego z podstawowych interfejsów COM były minimalne i ortogonalne.

W związku z popularnością formantów COM i ActiveX architektura .NET została zbudowana do obsługi COM IUnknown, IClassFactory i ITypeLibrary. W modelu COM interfejsy te niekoniecznie dotyczyły tego samego obiektu, więc połączenie ich ze sobą niekoniecznie miało sens.

Burt_Harris
źródło