Подтвердить что ты не робот

Почему я не должен использовать "с" в Delphi?

Я слышал много программистов, особенно программисты Delphi презирают использование "с".

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

Вот пример:

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;

Мне нравится использовать with. Что случилось со мной?

4b9b3361

Ответ 1

Одно раздражение при использовании с - это то, что отладчик не может его обработать. Это затрудняет отладку.

Большая проблема заключается в том, что читать код легче. Особенно, если оператор with немного длиннее.

procedure TMyForm.ButtonClick(...)
begin
  with OtherForm do begin
    Left := 10;
    Top := 20;
    CallThisFunction;
  end;
end;

Какую форму CallThisFunction вызывается? Self (TMyForm) или OtherForm? Вы не можете знать, не проверяя, имеет ли метод OtherForm метод CallThisFunction.

И самая большая проблема заключается в том, что вы можете легко создавать ошибки, даже не зная об этом. Что делать, если у TMyForm и OtherForm есть функция CallThisFunction, но она закрыта. Вы можете ожидать/хотите вызвать функцию OtherForm.CallThisFunction, но это действительно так. Компилятор предупредил бы вас, если вы не использовали команду with, но теперь это не так.

Использование нескольких объектов в с и умножает проблемы. См. http://blog.marcocantu.com/blog/with_harmful.html

Ответ 2

Я предпочитаю синтаксис VB в этом случае, потому что здесь вам нужно префикс элементов внутри блока с помощью ., чтобы избежать двусмысленностей:

With obj
    .Left = 10
    .Submit()
End With

Но на самом деле нет ничего плохого в with вообще.

Ответ 3

Было бы здорово, если оператор with был бы расширен следующим образом:

with x := ARect do
begin
  x.Left := 0;
  x.Rigth := 0;
  ...
end;

Вам не нужно объявлять переменную 'x'. Он будет создан компилятором. Он быстро пишет и не путает, какая функция используется.

Ответ 4

Неверно, что "с" сделает код более быстрым, более вероятно, что компилятор скомпилирует его с тем же исполняемым кодом.

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

Есть случаи, когда это реальная проблема, и случаи, когда это не проблема (случаи без проблем будут такими, как описано в вопросе, как "разумно" ).

Из-за возможной путаницы некоторые разработчики предпочитают воздерживаться от использования "с" полностью, даже в тех случаях, когда не может быть такой путаницы. Это может показаться догматичным, однако можно утверждать, что по мере того, как код изменяется и растет, использование "с" может оставаться даже после того, как код был изменен до такой степени, что сделает "с" запутанным, и, следовательно, лучше не ввести его использование в первую очередь.

Ответ 5

Фактически:

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;

и

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  FillRectS(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, Value);
end;

Будет генерировать точно такой же код ассемблера.

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

Фактически:

with MyRect do
begin
  Left := 0;
  Right := 0;
end;

кодируется в псевдокоде как таковой компилятором:

var aRect: ^TRect;

aRect := @MyRect;
aRect^.Left := 0;
aRect^.Right := 0;

Тогда aRect может быть просто регистром процессора, но также может быть истинной временной переменной в стеке. Конечно, я использую указатели здесь, так как TRect есть record. Он более прямой для объектов, поскольку они уже являются указателями.

Лично я использовал иногда в своем коде, но я почти проверяю каждый раз, когда создается asm, чтобы убедиться, что он делает то, что должен. Не каждый может или имеет время сделать это, поэтому ИМХО является локальной альтернативой.

Мне действительно не нравится такой код:

for i := 0 to ObjList.Count-1 do
  for j := 0 to ObjList[i].NestedList.Count-1 do
  begin
    ObjList[i].NestedList[j].Member := 'Toto';
    ObjList[i].NestedList[j].Count := 10;
  end;

Он по-прежнему довольно читается с помощью:

for i := 0 to ObjList.Count-1 do
  for j := 0 to ObjList[i].NestedList.Count-1 do
  with ObjList[i].NestedList[j] do
  begin
    Member := 'Toto';
    Count := 10;
  end;

или даже

for i := 0 to ObjList.Count-1 do
  with ObjList[i] do
  for j := 0 to NestedList.Count-1 do
  with NestedList[j] do
  begin
    Member := 'Toto';
    Count := 10;
  end;

но если внутренний цикл огромен, локальная переменная имеет смысл:

for i := 0 to ObjList.Count-1 do
begin
  Obj := ObjList[i];
  for j := 0 to Obj.NestedList.Count-1 do
  begin
    Nested := Obj.NestedList[j];
    Nested.Member := 'Toto';
    Nested.Count := 10;
  end;
end;

Этот код не будет медленнее, чем with: компилятор действительно делает это за сценой!

Кстати, это позволит упростить отладку: вы можете поместить точку останова, а затем наведите указатель мыши на Obj или Nested, чтобы получить внутренние значения.

Ответ 6

Эта дискуссия также часто встречается в Javascript.

По сути, с помощью синтаксиса очень сложно сразу определить, какое свойство/метод Left/Top/etc вы вызываете. У вас может быть локальная переменная с именем Left и свойство (это было давно сделано delphi, извините, если имя неверное) называется Left, возможно, даже функция называется Left. Любой, кто читает код и не знаком со структурой ARect, может быть очень и очень потерян.

Ответ 7

Что вы сохраняете при наборе текста, вы теряете удобочитаемость. Многие отладчики не будут знать, что вы имеете в виду, так что отладка сложнее. Это не ускоряет выполнение программ.

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

Ответ 8

Это прежде всего проблема обслуживания.

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

Например, у нас есть небольшая функция foo, которая содержит три или четыре строки кода, которые были обернуты внутри блока WITH, тогда действительно нет проблемы. Однако через несколько лет эта функция, возможно, расширилась под несколькими программистами на 40 или 50 строк кода, все еще завернутых внутри СОМ. Это теперь хрупкое и созрело для появления ошибок, особенно, если звезды-сопровождающие внедряют дополнительные встроенные блоки.

WITH не имеет других преимуществ - код должен анализироваться точно так же и работать с одинаковой скоростью (я сделал некоторые эксперименты с этим в D6 внутри жестких циклов, используемых для 3D-рендеринга, и я не мог найти разницы). Неспособность отладчика справиться с этим также является проблемой, но она должна была быть исправлена ​​некоторое время назад и стоило бы проигнорировать, если бы была какая-либо польза. К сожалению нет.

Ответ 9

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

Ответ 10

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

Насколько мне известно, он ничего не ускоряет - это чисто синтаксический сахар.

Ответ 11

На работе мы даем очки для удаления Withs из существующей базы кодов Win 32 из-за дополнительных усилий, необходимых для поддержки кода, который их использует. Я обнаружил несколько ошибок в предыдущем задании, где локальная переменная с именем BusinessComponent была замаскирована, находясь внутри блока "С началом" для объекта, который опубликовал свойство BusinessComponent того же типа. Компилятор решил использовать опубликованное свойство и код, который должен был использовать локальную переменную.

Я видел код вроде

С a, b, c, d do {за исключением того, что они намного длиннее имен, просто сокращенных здесь) начать   i: = xyz;
конец;

Это может быть настоящая боль, пытаясь найти место, откуда приходит xyz. Если бы это было c, я бы скорее написал его как

i: = c.xyz;

Вы думаете, что это довольно тривиально, чтобы понять это, но не в функции, длина которой составляла 800 строк, которая использовалась с правом с самого начала!

Ответ 12

Вы можете комбинировать с утверждениями, поэтому вы получаете

with Object1, Object2, Object3 do
begin
  //... Confusing statements here
end

И если вы считаете, что отладчик смущен одним, я не вижу, как кто-то может определить, что происходит в блоке with

Ответ 13

Это позволяет некомпетентным или злым программистам писать код без чтения. Поэтому используйте эту функцию, если вы не являетесь ни некомпетентным, ни злым.

Ответ 14

... выполняется быстрее...

Не обязательно - ваш компилятор/интерпретатор, как правило, лучше оптимизирует код, чем вы.

Я думаю, это заставляет меня сказать "yuck!" потому что он ленив - когда я читаю код (особенно кто-то другой), мне нравится видеть явный код. Поэтому я даже написал "this.field" вместо "field" в Java.

Ответ 15

Мы недавно запретили его в наших программах кодирования Delphi.

Профи часто перевешивали минусы.

Это ошибки были введены из-за его неправильного использования. Это не оправдывало экономии времени для написания или выполнения кода.

Да, использование с помощью can привело к (более мягкому) ускорению выполнения кода.

В следующем случае foo оценивается только один раз:

with foo do
begin
  bar := 1;
  bin := x;
  box := 'abc';
end

Но здесь он оценивается три раза:

foo.bar := 1;
foo.bin := x;
foo.box := 'abc';

Ответ 16

Для Delphi 2005 существует жесткая ошибка в инструкции do-do - оценка указателя теряется и исчезает с указателем вверх. Нужно использовать локальную переменную, а не тип объекта напрямую.