Тим, хто знайомий з історією, добре відомо, що C # та .NET Framework почали створюватись як «Delphi переписаний, щоб відчути себе Java», архітектор головного розробника Delphi Андерса Хейлсберга. З тих пір речі значно розійшлися, але на початку подібність була настільки очевидною, що навіть існували серйозні міркування про те, що .NET насправді був продуктом Borland.
Але останнім часом я розглядав деякі речі .NET, і, здається, одна з найцікавіших і корисних функцій від Delphi повністю відсутня: концепція класів як першокласного типу даних. Для тих, хто з ним не знайомий, тип TClass
представляє посилання на клас, подібний до Type
типу в .NET. Але там, де .NET використовує Type
для роздумів, Delphi використовує TClass
як дуже важливу вбудовану частину мови. Він дозволяє використовувати різні корисні ідіоми, які просто не існують і не можуть існувати без нього, такі як змінні підтипи класів та методи віртуального класу.
У кожній мові ОО є віртуальні методи, в яких різні класи по-різному реалізують одне і те ж фундаментальне поняття методу, і тоді правильний метод викликається під час виконання на основі фактичного типу екземпляра об'єкта, на який він викликається. Delphi поширює це поняття на класи: якщо у вас є посилання TClass, визначений як специфічний підтип класу (тобто class of TMyClass
означає, що змінна може приймати будь-яку посилання класу, що успадковується від TMyClass
, але не що-небудь поза герархією), до якого додаються віртуальні методи класового діапазону це, їх можна викликати без екземпляра, використовуючи фактичний тип класу. Застосування цього шаблону до конструкторів, наприклад, робить тривиальну реалізацію заводу.
Здається, немає нічого еквівалентного в .NET. З настільки ж корисними, як і посилання на клас (і особливо віртуальні конструктори та інші методи віртуального класу!), Хтось сказав що-небудь про те, чому їх залишили?
Конкретні приклади
Десеріалізація форми
Delphi VCL зберігає форми у DFM
форматі, DSL для опису ієрархії компонентів. Коли зчитувач форми аналізує дані DFM, він проходить через об'єкти, описані так:
object Name: ClassName
property = value
property = value
...
object SubObjectName: ClassName
...
end
end
Цікавим тут є ClassName
частина. Кожен клас компонентів реєструє його TClass
разом із системою потокової передачі компонентів initialization
(мислити статичні конструктори, лише дещо інші, гарантовано відбудуться негайно під час запуску.) Цей регістр реєструє кожен клас у рядку-> хешмак TClass з ім'ям класу як ключовим.
Кожен компонент відбувається з TComponent
, який має віртуальний конструктор , який приймає один аргумент, Owner: TComponent
. Будь-який компонент може замінити цей конструктор, щоб забезпечити його власну ініціалізацію. Коли зчитувач DFM читає ім'я класу, він шукає ім'я у вищезгаданій хешмапі та отримує відповідну посилання класу (або збільшує виняток, якщо його немає), а потім викликає віртуальний конструктор TComponent на ньому, який, як відомо, є хорошим тому що функція реєстрації містить посилання на клас, необхідний для спуску з TComponent, і ви отримуєте об'єкт відповідного типу.
Не маючи цього, еквівалент WinForms - це ... ну ... великий безлад, щоб сказати це прямо, вимагаючи будь-якої нової мови .NET, щоб повністю реалізувати власну форму (де) серіалізацію. Це трохи шокує, коли ти думаєш про це; Оскільки суть CLR полягає в тому, щоб дозволити декілька мов використовувати одну і ту ж основну інфраструктуру, система DFM мала б ідеальний сенс.
Розширюваність
Клас менеджера зображень, про який я писав, може надавати джерело даних (наприклад, шлях до файлів зображень), а потім автоматично завантажувати нові об’єкти зображень, якщо ви намагаєтеся отримати ім’я, яке не знаходиться в колекції, але воно доступне у джерелі даних. Він має змінну класу, введену як class of
базовий клас зображення, що представляє клас будь-яких нових об'єктів, які слід створити. Він поставляється за замовчуванням, але є деякі моменти, коли створюються нові зображення із спеціальними цілями, що зображення слід налаштовувати різними способами. (Створення його без альфа-каналу, отримання спеціальних метаданих з файлу PNG для визначення розміру спрайту тощо)
Це можна зробити, записавши велику кількість конфігураційного коду та передавши спеціальні параметри всім методам, які могли б у результаті створити новий об'єкт ... або ви могли просто створити підклас базового класу зображень, який перекриває віртуальний метод, де відповідний аспект конфігурується, а потім використовуйте блок "спробувати / остаточно", щоб тимчасово замінити властивість "класу за замовчуванням" за необхідності та відновити його. Зробити це за допомогою змінних посилань класу набагато простіше, і це не те, що можна зробити замість генеричних даних.
TClass
корисний, з деяким зразком коду? У ході мого короткого дослідження в Інтернеті TClass
я виявляю, що TClass
його можна передати як параметр. Це робиться в .NET за допомогою Generics. Фабричні методи просто позначені static
у .NET, і для цього не потрібен екземпляр класу.
TClass
це основна мовна особливість, яка потребує підтримки компілятора. Ви не можете "написати свій" без написання власної мови, а для .NET навіть цього було б недостатньо, оскільки об'єктна модель визначається CLR, а не окремими мовами. Це щось, що буквально повинно бути частиною самої основи .NET, або воно не може існувати.