Методы класса в записи против класса в Delphi 2010 - PullRequest
17 голосов
/ 07 марта 2011

Я только начал играть с новым модулем в Delphi 2010 IOUtils.pas, и обнаружил, что они помещают все методы в Records(TFile, TPath, TDirectory) как функции и процедуры класса.

Есть ли какая-то польза от этого внутри записей вместо классов? В обоих случаях нет необходимости в каких-либо переменных или экземплярах, но я не уверен, есть ли какие-либо реальные преимущества в отношении потребления памяти или улучшения производительности.

Ответы [ 4 ]

21 голосов
/ 07 марта 2011

Методы класса в записях используются для группировки различных методов в общее пространство имен.Таким образом, вы можете иметь похожие именованные методы для разных целей.Для примера в IOUtils.pas посмотрите на функцию Exists , доступную в TFile и в TDirectory.Более старый подход состоял в том, чтобы иметь разные имена функций для FileExists и DirectoryExists (которые фактически вызывают реализации).

Хотя методы класса внутри классов могут использоваться одинаково, они могут, кроме того, иметь другую цель:быть виртуальнымВызванный из переменной класса, это может привести к различным реализациям в зависимости от текущего содержимого этой переменной.Это невозможно для записей.Как следствие, методы класса в записях всегда статичны.

9 голосов
/ 08 марта 2011

Я бы сказал, что первый вопрос, который нужно задать: «Зачем помещать такие функции внутри записи или класса?»во-первых.

Использование методов записи, как в IOUtils , во многих случаях во многом произвольно в Delphi и частично отражает фетиш со следующими соглашениями в других языках, которые не имеют прямого отношенияв Delphi.

В некоторых языках (Java и .NET?) функции «первого класса» не поддерживаются.То есть процедуры и функции не могут существовать независимо от какого-либо класса контейнера.Следовательно, в этих языках, если у вас есть функции и процедуры, они должны содержаться в классе.Если они не работают с данными-членами, то, очевидно, они объявляются как методы класса, чтобы избежать необходимости конструировать экземпляр просто для вызова того, что на самом деле является функцией classLESS.

т.е.

TDirectory.Exists (s) и TFile.Exists (s) являются синтаксическими альтернативами DirectoryExists (s) и FileExists (s) .

В 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 и не нуждается в уточнении и повторении этого факта нигде, кроме как в условии использования.

AПотенциальная и очень РЕАЛЬНАЯ опасность здесь, конечно, заключается в том, что в будущем, если модуль будет расширен и впоследствии начнет использовать IOUtils.File , это может легко привести к ошибкам определения области на любых неквалифицированных ссылках.

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

По этим причинам выбор дизайнав IOUtils , по крайней мере, понятно и, возможно, хорошо, хотя ymmv.

Но в других случаях, если все, что вы ищете, это способ сгруппировать связанные функции, которые не подвержены рискупутаница / столкновение с аналогичными функциями, тогда использование искусственного и произвольного контейнера является излишним.Модуль, в котором должны находиться функции, сам может обеспечить все необходимое поведение при группировании.

Что касается записи, а не класса ...

Я подозреваю, что при изготовлении этих произвольных контейнеров может быть небольшое преимущество статической памяти при использовании записи, а не класса. Я полагаю, что у класса есть дополнительные издержки, хотя он будет относительно небольшим и статичным по природе (то есть немного больше накладных расходов для каждого класса, но не связан с тем, сколько раз его класс использует методы ).

4 голосов
/ 07 марта 2011

Методы статического класса являются просто синтаксическим сахаром - вы всегда можете реализовать их как обычные процедуры / функции.

Если метод класса реализован внутри записи, это приводит к меньшим издержкам.Любое объявление класса требует дополнительной структуры для информации о классе в памяти.Записи не имеют структуры.

2 голосов
/ 19 ноября 2011

По-моему, нужна очень веская причина для использования записей над классами со статическими методами.Как для TValue, когда скорость имеет значение.Например, просто получается, что TDirectory.CreateDirectory () просто не работает для путей UNC, а TPath.DriveExists () - для пути UNC.(D2010) Если бы это был класс, я бы просто написал вспомогательный класс, реализующий мой собственный TDirectory.CreateDirectoryEx ().Так как это запись, я не могу скопировать какой-то код из IOUtils или написать свой собственный.В любом случае я в конечном итоге с избыточностью кода.Так что с точки зрения расширяемого кода выбор записей - плохая идея.(Я на самом деле все еще удивляюсь, почему VCL-Team решила не учитывать расширяемость. Я имею в виду сравнение VCL с QuantumGrid DevEx в этом аспекте. Между ними есть светлые годы)на самом деле написать помощник для записи:

TDirectoryHelper = record helper for TDirectory

end;

Но это не очень помогает, так как вы не можете получить доступ к любому частному статическому методу из TDirectory

...