Почему .NET Framework не имеет понятия классов как первоклассных типов?

20

Тем, кто знаком с историей, хорошо известно, что C # и .NET Framework начинались как «Delphi, переписанный для ощущения Java», созданный главным разработчиком Delphi Андерсом Хейлсбергом. С тех пор все немного изменилось, но на самом деле сходство было настолько очевидным, что даже были серьезные предположения, что .NET изначально был продуктом Borland.

Но в последнее время я изучал некоторые вещи .NET, и одна из самых интересных и полезных функций Delphi, похоже, полностью отсутствует: концепция классов как первоклассного типа данных. Для тех, кто не знаком с ним, тип TClassпредставляет ссылку на класс, похожий на Typeтип в .NET. Но где .NET использует Typeдля отражения, Delphi использует TClassв качестве очень важной встроенной части языка. Он учитывает различные полезные идиомы, которые просто не существуют и не могут существовать без него, такие как переменные подтипов классов и методы виртуальных классов.

Каждый ОО-язык имеет виртуальные методы, в которых разные классы по-разному реализуют одну и ту же фундаментальную концепцию метода, а затем во время выполнения вызывается правильный метод на основе фактического типа экземпляра объекта, к которому он обращен. Delphi распространяет эту концепцию на классы: если у вас есть ссылка на TClass, определенная как определенный подтип класса (то есть class of TMyClassозначает, что переменная может принимать любую ссылку на класс, которая наследуется TMyClass, но не что-либо вне иерархии), к которой присоединены виртуальные методы области класса это, они могут быть вызваны без экземпляра, используя фактический тип класса. Например, применение этого шаблона к конструкторам делает реализацию Factory тривиальной.

Кажется, в .NET нет ничего эквивалентного. Как полезны ссылки на классы (особенно на виртуальные конструкторы и другие методы виртуальных классов!), Кто-нибудь говорил что-нибудь о том, почему они были исключены?

Конкретные примеры

Форма десериализации

Delphi VCL сохраняет формы в DFMформате, DSL для описания иерархии компонентов. Когда средство чтения форм анализирует данные DFM, оно наталкивается на объекты, которые описаны следующим образом:

object Name: ClassName
   property = value
   property = value
   ...
   object SubObjectName: ClassName
      ...
   end
end

Интересная вещь здесь - ClassNameчасть. Каждый класс компонента регистрирует его TClassв системе потоковой передачи компонента initializationодновременно (представьте, что статические конструкторы, только немного отличающиеся, гарантированно произойдут сразу при запуске). Это регистрирует каждый класс в хеш-таблице string-> TClass с именем класса в качестве ключа.

Каждый компонент происходит от TComponent, у которого есть виртуальный конструктор, который принимает один аргумент Owner: TComponent. Любой компонент может переопределить этот конструктор, чтобы обеспечить собственную инициализацию. Когда читатель DFM читает имя класса, он ищет имя в вышеупомянутой хэш-карте и получает соответствующую ссылку на класс (или вызывает исключение, если его там нет), а затем вызывает для него виртуальный конструктор TComponent, который, как известно, является хорошим потому что функция регистрации принимает ссылку на класс, которая требуется для наследования от TComponent, и в результате вы получаете объект соответствующего типа.

Без этого, WinForms-эквивалент - это ... ну ... большой беспорядок, если говорить прямо, требуя, чтобы любой новый язык .NET полностью заново реализовывал свою собственную форму (де) сериализацию. Это немного шокирует, когда вы думаете об этом; Поскольку весь смысл наличия CLR состоит в том, чтобы позволить нескольким языкам использовать одну и ту же базовую инфраструктуру, система в стиле DFM имела бы смысл.

растяжимость

Класс менеджера изображений, который я написал, может быть снабжен источником данных (например, путем к файлам изображений), а затем автоматически загружать новые объекты изображений, если вы пытаетесь получить имя, которого нет в коллекции, но которое доступно в источнике данных. Он имеет переменную класса, типизированную как class ofбазовый класс изображения, представляющий класс любых новых создаваемых объектов. Он поставляется по умолчанию, но есть некоторые моменты при создании новых изображений со специальными целями, что изображения должны быть настроены по-разному. (Создание его без альфа-канала, получение специальных метаданных из файла PNG для указания размера спрайта и т. Д.)

Это может быть сделано путем написания большого количества кода конфигурации и передачи специальных параметров всем методам, которые могут в конечном итоге создать новый объект ... или вы можете просто создать подкласс базового класса изображений, который переопределяет виртуальный метод, где рассматриваемый аспект настраивается, а затем с помощью блока try / finally временно заменяет свойство «класс по умолчанию», а затем восстанавливает его. Делать это со ссылочными переменными класса гораздо проще, и это не то, что можно сделать с помощью обобщений.

Мейсон Уилер
источник
3
Можете ли вы привести один или два конкретных примера, где TClassполезно, с некоторым примером кода? В моем беглом интернет-исследовании TClassя обнаружил, что это TClassможно передать как параметр. Это делается в .NET с использованием Generics. Методы фабрики просто помечены staticв .NET и не требуют экземпляра класса для выполнения.
Роберт Харви
7
@RobertHarvey: Лично для меня C # стал намного более понятным, когда я перестал смотреть на него, когда Java / C ++ перезагружался, и начал рассматривать его как Modula-2 с объектами. То же самое с Java: все говорят, что на него повлиял C ++, но это не так. Его основное влияние Objective-C (и Smalltalk через это), и Java будет сделать гораздо больше смысла , когда вы рассматривать его как Smalltalk с типами вместо C ++ с GC.
Йорг Миттаг
3
@RobertHarvey: TClassфундаментальная языковая функция, требующая поддержки компилятора. Вы не можете «написать свой собственный», не написав свой собственный язык, и для .NET даже этого будет недостаточно, поскольку объектная модель определяется CLR, а не отдельными языками. Это то, что буквально должно быть частью самой платформы .NET, иначе оно не может существовать.
Мейсон Уилер
4
Прежде всего, я могу с полной уверенностью сказать, что .NET изначально не был продуктом Borland. Откуда я это знаю? Я был (все еще) частью первоначальной основной команды, которая разработала Delphi. Я тесно сотрудничал с Андерсом, Чаком, Гари и другими. Конечно, я уверен, что вы это знаете. Что касается существования ссылки на класс (как называются TClass и аналогичные конструкции) в .NET, вероятно, было сочтено ненужным из-за существования богатой информации о доступных во время выполнения типах. В начале у Delphi было гораздо меньше информации о типах, а ссылки на классы были компромиссом.
Аллен Бауэр

Ответы:

5

.NET (CLR) было третьим поколением компонентной объектной модели (COM) Microsoft, которая в первые дни называлась «среда выполнения COM +». Microsoft Visual Basic и рынок элементов управления COM / ActiveX оказали гораздо большее влияние на конкретные варианты совместимости архитектур CLR, чем Borland Delphi. (Следует признать, что тот факт, что Delphi принял элементы управления ActiveX, безусловно, способствовал развитию экосистемы ActiveX, но COM / ActiveX существовал до Delphi)

Архитектура COM была разработана на C (не C ++) и ориентирована на интерфейсы, а не на классы. Кроме того, поддерживается составление объектов, что означает, что COM-объект может фактически состоять из нескольких различных объектов, а интерфейс IUnknown связывает их вместе. Но IUnknown не играл никакой роли в создании COM-объекта, который был разработан так, чтобы быть максимально независимым от языка. Создание объекта обычно осуществлялось IClassFactory, а отражение - ITypeLibrary и связанными интерфейсами. Такое разделение интересов не зависело от языка реализации, а возможности каждого основного COM-интерфейса были минимальными и ортогональными.

Таким образом, в результате популярности элементов управления COM и ActiveX была создана архитектура .NET для поддержки COM IUnknown, IClassFactory и ITypeLibrary. В COM эти интерфейсы не обязательно должны быть на одном и том же объекте, поэтому объединение их не обязательно имеет смысл.

Burt_Harris
источник