Есть ли преимущества в том, что внутри записей вместо классов?
В обоих случаях нет необходимости в каких-либо переменных или экземплярах, но я не уверен, есть ли какие-либо реальные преимущества в отношении потребления памяти или улучшения производительности.
Ответ 2
Я бы сказал, что первый вопрос, который нужно задать, - "Зачем ставить такие функции внутри записи или класса?" в первую очередь.
Использование методов записи, как в IOUtils, во многих случаях во многом является произвольным в Delphi и частично отражает фетиш со следующими соглашениями на других языках, которые не имеют прямого отношения к Delphi.
В некоторых языках (Java и .NET?) функции "первого класса" не поддерживаются. То есть процедуры и функции не могут существовать независимо от любого класса контейнера. Следовательно, на этих языках, если у вас есть функции и процедуры, они должны содержаться в классе. Если они не работают с данными-членами, то, очевидно, они объявляются как методы класса, чтобы избежать необходимости создавать экземпляр, просто чтобы вызвать то, что на самом деле является функцией classLESS.
то есть.
TDirectory.Exists(s) и TFile.Exists(s) являются синтаксическими альтернативами DirectoryExists (s) и FileExists (ы).
В функциях первого класса Delphi поддерживаются, и поэтому нет реальной причины использовать классы или записи для различения функций, кроме как способ упрощения ссылки для разработчика при написании кода и используя поддержку IDE для поиска доступных методов.
Без записи/класса контейнера вы должны знать, что существует функция, называемая "DirectoryExists()". С записью контейнера вам нужно только знать, что существует тип TDirectory, а среда IDE (через предложения по завершению кода) может представить все "методы", доступные для операций TDirectory.
Однако в Delphi существует альтернатива, обеспечиваемая поддержкой функций первого класса. Вместо использования произвольных и искусственных "контейнеров" вы (или, скорее, авторы IOUtils) могли вместо этого использовать существующий контейнер - блок Delphi:
unit IOUtils.Directory;
interface
function Exists(s): Boolean;
и
unit IOUtils.File;
interface
function Exists(s): Boolean;
Это по-прежнему пользуется преимуществами поддержки IDE в виде предложений о завершении кода, а также обеспечивает разделение пространства имен, необходимое для поддержки идентичных имен функций, работающих на разных вещах.
Недостатком этого подхода (и он является большим) является то, что, когда единица использует как IOUtils.File, так и IOUtils.Directory, он должен квалифицировать один или другое имя функции, чтобы избежать путаницы, но эта квалификация может быть опущена, поэтому она настолько опасна:
uses
IOUtils.File,
IOUtils.Directory;
..
begin
if Exists(s) then // << tests existence of a directory
..
if IOUtils.File.Exists(s) then // << ensure we test for a file
..
if IOUtils.Directory.Exists(s) then // << ensure we test for a directory
end
Это также может быть преимуществом, конечно, если ваш блок содержит код, который ТОЛЬКО работает с каталогами, он будет использовать только IOUtils.Directory и не должен квалифицироваться и повторять этот факт где угодно, кроме как в uses.
Конечно, потенциальная и очень реальная опасность заключается в том, что в будущем, если устройство будет расширено и затем начнет использовать IOUtils.File, это может легко привести к ошибкам определения области охвата на любых неквалифицированных ссылках.
Какой подход, который вы предпочитаете в своем коде, будет во многом зависеть от личных предпочтений и возможности для коллизий в ошибках определения области видимости, где у вас есть похожие/идентично названные или семантически эквивалентные функции, работающие на разных объектах.
По этим причинам выбор дизайна в IOUtils по крайней мере является понятным и, возможно, хорошим, tho ymmv.
Но в других случаях, если все, что вы ищете, - это способ группировки связанных функций, которые не подвержены риску путаницы/столкновения с аналогичными функциями, тогда использование искусственного и произвольного контейнера является излишним. Единица, в которой функции должны находиться внутри, сама может обеспечить все необходимые вам группировки.
Что касается того, почему запись, а не класс...
Я подозреваю, что при изготовлении этих произвольных контейнеров может быть небольшая статическая память для использования записи, а не класса. Я полагаю, что класс имеет дополнительные накладные расходы, это будет относительно небольшим и статическим по своей природе (т.е. Немного больше накладных расходов для каждого класса, но не связано с тем, сколько раз использовались его методы класса).
Ответ 4
IMO, для использования записей по классам со статическими методами потребуется очень хорошая причина. Как для TValue, когда скорость имеет значение.
Например, просто получается, что TDirectory.CreateDirectory() просто не работает для UNC-путей, потому что TPath.DriveExists() не работает для UNC-пути. (D2010)
Если бы это был класс, я бы просто написал вспомогательный класс, реализующий мой собственный TDirectory.CreateDirectoryEx().
Поскольку его запись я не могу и либо скопирую код из IOUtils, либо напишу свой собственный. В любом случае я получаю избыточность кода.
Так что с точки зрения выдающегося кода выбор записей - плохая идея.
(На самом деле я до сих пор удивляюсь, почему команда VCL решила не рассматривать расширяемость. Я имею в виду сравнение VCL с DevEx QuantumGrid в этом аспекте. Между ними есть свет)
Хорошо, я только что узнал, что вы можете написать хелпер для записи:
TDirectoryHelper = record helper for TDirectory
end;
Но это не очень помогает, поскольку вы не можете получить доступ к какому-либо приватному статическому методу из TDirectory