Является ли Delphi ключевым словом «с» плохой практикой?

30

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

Я часто помещаю все свои TClientDataSets и TFields в TDataModules. Так что в моих формах у меня был код вроде этого

procedure TMyForm.AddButtonClick(Sender: TObject);
begin  
  with LongNameDataModule do
  begin
     LongNameTable1.Insert;
     LongNameTable1_Field1.Value := "some value";
     LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
     LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
     LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
     LongNameTable1.Post;
  end
end;

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

    procedure TMyForm.AddButtonClick(Sender: TObject);
    begin            
      LongNameDataModule.LongNameTable1.Insert;
      LongNameDataModule.LongNameTable1_LongNameField1.Value := "some value";

      LongNameDataModule.LongNameTable1_LongNameField2.Value :=
               LongNameDataModule.LongNameTable2_LongNameField1.Value;

      LongNameDataModule.LongNameTable1_LongNameField3.Value :=
               LongNameDataModule.LongNameTable3_LongNameField1.Value;

      LongNameDataModule.LongNameTable1_LongNameField4.Value :=
               LongNameDataModule.LongNameTable4_LongNameField1.Value;

      LongNameDataModule.LongNameTable1.Post;
    end;

Мне кажется, что читать легче, используя ключевое слово с.

Не следует ли использовать ключевое слово с?

  • 0
    Хотя я согласен с вашим использованием в этом случае - без этого способа использования модулей данных не было бы необходимости, что нарушает Закон Деметры. Delphi требует, чтобы все компоненты в модуле формы / данных были общедоступными, но лучше не использовать это (слишком много).
  • 1
    Дубликат: stackoverflow.com/questions/71419/whats-wrong-with-delphis-with
Показать ещё 2 комментария
Теги:
with-statement

14 ответов

56

Самая большая опасность, связанная с такими патологическими состояниями, как "с A, B, C, D", заключается в том, что ваш код может молча изменить смысл без вашего уведомления. Рассмотрим этот пример:

with TFoo.Create
try
  Bar := Baz;
  DoSomething();
finally
  Free;
end;

Вы пишете этот код, зная, что Bar является свойством TFoo, а Baz является свойством типа, содержащего метод, который имеет этот код.

Теперь, через два года, появляется какой-то благонамеренный разработчик, добавляет свойство Baz в TFoo. Ваш код молча изменил значение. Компилятор не будет жаловаться, но код теперь сломан.

  • 15
    Бррр ... Это действительно ужасно.
28

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

Debugging:

При использовании этого кода:

with TMyClass.Create do
try
  Add('foo');
finally
  Free;
end;

Нет способа проверить свойства этого класса, поэтому всегда объявляйте переменную и используйте ключевое слово с.

Интерфейсы:

При создании интерфейса в предложении с он живет до конца вашего метода:

procedure MemoryHog;
begin
  with GetInterfaceThatTakes50MBOfMemory do
    Whatever;
  ShowMessage('I''m still using 50MB of memory!');
end;

Ясность

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

with TMyForm.Create do
  Width := Width + 2; //which width in this with is width?

Конечно, если у вас есть повторяющиеся имена, вы используете свойства и методы класса, объявленного в вашем операторе with (TMyForm).

  • 1
    Конструкция 'with' чертовски запутала отладчик в Delphi 7. Я не использовал другие версии Delphi и не знаю, улучшилось ли это плохое поведение
  • 1
    По крайней мере, до BDS2006, он не улучшился. D2007 и после, я не знаю.
Показать ещё 1 комментарий
23

У оператора with есть свое место, но я должен согласиться с тем, что чрезмерное использование может привести к двусмысленному коду. Хорошее эмпирическое правило состоит в том, чтобы убедиться, что код "более" доступен для чтения и поддержки после добавления оператора with. Если вы чувствуете, что вам нужно добавить комментарии, чтобы объяснить код после добавления инструкции, это, вероятно, плохая идея. Если код более читабельен, как в вашем примере, используйте его.

btw: это всегда был один из моих любимых шаблонов в Delphi для показа модального окна

with TForm.Create(nil) do
try
  ShowModal;
finally
  Free;
end
  • 1
    Согласовано. Использование «с» - это хорошая и плохая привычка. Через некоторое время вы узнаете, где провести черту.
  • 9
    Другим решением для модальных форм было бы добавление нового статического метода «CreateShowModalAndFree»;) ...
Показать ещё 1 комментарий
13

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

Код Брайана кажется читабельным и приятным, но код будет короче, если вы просто придумаете отправителя напрямую, и вы устраните все сомнения по поводу того компонента, который вы включили:

TAction(Sender).Enabled := Something;

Если вы беспокоитесь о многократном вводе, я предпочитаю временно ссылаться на объект с длинными именами:

var
  t: TTable;
begin
  t := theLongNamedDataModule.WithItsLongNamedTable;
  t.FieldByName(' ');
end;

Я не могу сказать, почему печатание должно вас беспокоить. Мы, во-первых, программисты, программисты Second, и завершение кода, копирование и запись клавиш могут помочь вам стать более эффективной машинистом.

обновление: Просто наткнулся на длинную статью с небольшим разделением на заявления: он с ключевым словом. Самая отвратительная, опасная, ударная сила вашего языка на этом языке.: -)

  • 0
    Я боюсь (чтобы быть педантичным), это должно быть типом как (Отправитель как TAction) .Enabled: = Что-то, поскольку это выполнит проверку во время выполнения, что Отправитель спускается из TAction и если не вызовет приятную ошибку, прямая типизация потерпит крах.
  • 0
    Но почему отправителем должно быть что-то кроме TAction? Если я использую один и тот же обработчик событий для разных типов компонентов, мне понадобится проверить, какой тип отправителя сгенерировал событие, но когда я подключаю событие к одному специальному компоненту, почему я должен ожидать чего-то еще в качестве отправителя?
Показать ещё 4 комментария
8

Когда я впервые начал программировать на паскале (с TurboPascal!) и узнал, как я пошел, WITH казался замечательным. Как вы говорите, ответ на утомительную типизацию и идеально подходит для этих длинных записей. Поскольку Delphi прибыл, я удалял его и поощрял других отказаться от него - аккуратно подвел Verity в в регистре Помимо уменьшения удобочитаемости, есть две основные причины, по которым я мог бы избежать этого:

  • Если вы используете класс, тогда вам это все равно не нужно - только записи "кажутся" полезными.
  • Использование отладчика для выполнения кода с объявлением с помощью Ctrl-Enter не работает.

Тем не менее, для удобства чтения я все еще использую синтаксис:

procedure ActionOnUpdate( Sender : TObject )
begin
  With Sender as TAction do
    Enabled := Something
end;

Я не видел лучшей конструкции.

  • 0
    Во- TAction(Sender).Enabled := Something; , TAction(Sender).Enabled := Something; мое предпочтение читабельности и ясности.
6

Ваш пример, доступ к датамному доступу в нажатие кнопки, на мой взгляд - плохо надуманный пример. Вся потребность в WITH уходит, если вы переместите этот код в модуль данных, где он должен быть. Затем OnClick вызывает LongNameDataModule.InsertStuff и нет необходимости.

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

5

Как упоминал Vegar, он так же аккуратен и намного читабельнее, легче отлаживается и менее подвержен проблемам скрытности, чтобы использовать временную ссылку.

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

Явный код просто читаем. Таким образом, вы можете получить свой торт и наслаждаться его едой.

procedure TMyForm.AddButtonClick(Sender: TObject);
var
  dm: TLongNameDataModuleType
begin  
  dm:=LongNameDataModule;

  dm.LongNameTable1.Insert;
  dm.LongNameTable1_Field1.Value := "some value";
  dm.LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
  dm.LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
  dm.LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
  dm.LongNameTable1.Post;
end;
  • 1
    Двойной с злом.
3

Ваш вопрос - отличный пример того, что "молот - это не всегда решение".

В этом случае "с" не является вашим решением: вы должны перенести эту бизнес-логику из своей формы в свой datamodule. Не делает этого, нарушает Закон Деметры, как mghie (Michael Hieke) уже прокомментировал.

Возможно, ваш пример был просто иллюстративным, но если вы действительно используете такой код в своих проектах, это то, что вы должны сделать вместо:

procedure TLongNameDataModule.AddToLongNameTable1(const NewField1Value: string);
begin  
  LongNameTable1.Insert;
  LongNameTable1_Field1.Value := NewField1Value;
  LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value;
  LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value;
  LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value;
  LongNameTable1.Post;
end;

И затем назовите его из вашей формы следующим образом:

procedure TMyForm.AddButtonClick(Sender: TObject);
begin  
  LongNameDataModule.AddToLongNameTable1('some value');
end;

Это эффективно избавляет вас от вашего заявления и делает ваш код более удобным в обслуживании одновременно.

Конечно, окружающие строки Delphi с одинарными кавычками помогут скомпилировать его; -)

  • 2
    Я собирался написать точно то же самое, теперь, когда я прочитал этот ответ, мне больше не нужно это делать. Пример был недоволен, это явное нарушение закона Деметры. Но полемика Arround with выходит за рамки этого, с другими аргументами
3

Я твердо убежден в том, что удаляю поддержку WITH в Delphi. Ваш пример использования использования datamodule с именованными полями - это единственный экземпляр, который я мог видеть, что он работает. В противном случае лучший аргумент против него дал Крейг Стунц - который я проголосовал.

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

3

Насколько мне известно, With вполне приемлемо в том случае, когда вы даете. Это, безусловно, улучшает четкость кода.

Реальное зло - это когда у вас сразу несколько открытий.

Кроме того, мое мнение заключается в том, что то, что вы используете с on, имеет большое значение. Если это действительно другой объект, то с, вероятно, плохая идея. Тем не менее, мне не нравится иметь много переменных на одном уровне, даже если это имеет смысл - как правило, объекты данных, которые содержат весь очень сложный элемент данных, - это, как правило, весь фрагмент работы, для которой программа предназначена для работы. (Я не думаю, что этот случай произошел бы в приложении, у которого не было такого элемента.) Чтобы сделать мир более ясным, я часто использую записи для группировки связанных элементов. Я считаю, что почти все, с которыми я пользуюсь, предназначены для доступа к таким подгруппам.

3

Основная проблема с "с" заключается в том, что вы не знаете, где заканчивается его область действия, и вы можете иметь несколько совпадающих с операторами.

Я не думаю, что вам следует избегать его использования, если ваш код доступен для чтения.

Одно из предложений сделать его более читаемым (и менее запутанным в более длинном коде) было, если codegear добавила опцию , чтобы разрешить псевдонимы внутри, и, возможно, разрешить несколько сеансов в одном:

procedure TMyForm.AddButtonClick(Sender: TObject);
begin  
  with LongNameDataModule as dm, dm.LongNameTable1 as t1, dm.LongNameTable2 as t2 do
  begin
    t1.Insert;
    t1.FieldByName('Field1').AsString := 'some value';
    t1.FieldByName('Field2').AsString := t2.FieldByName('Field2').AsString;
    t1.Post;
    dm.Connection.Commit;
  end
end;
  • 0
    В примере я использую TField вместо метода FieldByName, поэтому мне не нужен псевдоним для LongNameTable2. Я могу просто использовать dm.LongTable2_Field1.Value. Спасибо за ответ
  • 0
    Использование «AS» таким образом может привести к неоднозначности (для чтения, а не для компилятора). Является ли DM типом, к которому вы привели LongNameDataModule, или DM является псевдонимом? Вы не можете сказать, просто глядя на линию. Лучшим подходом (IMNSHO) было бы «копировать» синтаксис из предложений EXCEPT: WITH DM: LongNameDataModule , возможно (чтобы сделать его более похожим на Pascal) WITH DM: = LongNameDateModule . Кроме того, DM не нужно объявлять (как переменные исключений), и он должен иметь только область видимости внутри WITH.
2

Здесь есть много превосходных ответов о том, почему утверждение with плохо, поэтому я постараюсь не повторять их. Я много лет использую эту инструкцию, и я очень сильно уклоняюсь от нее. Это частично из-за того, что может быть сложно разобраться в объеме, но в последнее время я начинаю заниматься рефакторингом, и ни один из автоматизированных рефакторингов не работает с выражением - и автоматическое рефакторинг является удивительным.

Также некоторое время назад я сделал видеоролик о том, почему утверждение with is bad, это не одна из моих лучших работ, но Здесь это

0

Используйте with только временно (так же, как вы закомментировать временно).

Это помогает вам писать эскизы кода, чтобы получить что-то скомпилированное и быстро выполняемое. Если вы консолидируете решение, очистите его! Удалить with, как вы переместите код в нужное место.

  • 1
    Нет, не делай этого. Вы никогда не будете делать уборку, потому что никогда не будет времени для этого.
  • 0
    @dummzeuch вы, вероятно, правы. И это то, что я хотел сказать по аналогии с комментариями;) - но, честно говоря: если у вас есть правила (или, что лучше, автоматические проверки), которые предотвращают фиксацию кода с with в хранилище, вы будете в безопасности. боковая сторона. Может быть, я должен добавить еще несколько слов? Спасибо в любом случае для комментируя мои объяснения возможного намерения позади with
Показать ещё 2 комментария
-2

Текущая инструкция With "опасна", но ее можно существенно улучшить:

  With TForm1.Create (Nil) Do  // New TForm1 instance
    Try
      LogForm (");  // That same instance as parameter to an outer method
      "ShowModal;  // Instance.ShowModal
    Finally
      "Free;  // Instance.Free
    End;

Мое предложение:

  • Не более одного объекта/записи в заголовке.
  • Вложенные не разрешены.
  • Использование "для указания объекта/записи (двойные кавычки аналогичны  к знаку ditto: http://en.wikipedia.org/wiki/Ditto_mark).
  • 2
    Это не отвечает на вопрос вообще.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню