2008-09-16 8 views
18

Я слышал много программистов, особенно программисты Delphi презирают использование «с».Почему я не должен использовать «с» в Delphi?

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

Вот пример:

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

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

ответ

2

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

+6

Это звучит как проблема с отладчиком, а не проблема с языковой функцией. – 2008-09-16 11:40:12

+0

@GregHewgill Вы правы, это проблема с отладчиком. Но это по-прежнему совершенно разумная причина, чтобы избежать его использования. Кроме того, это не оправдывает все проблемы с языковыми функциями ** с помощью **. Самое большое существо, которое ** с ** фактически ** _ уменьшает читаемость **. ПРИМЕЧАНИЕ. Меньший код (с использованием ** с **) не улучшает читаемость автоматически. Он уменьшает его, потому что он больше не является явным, о чем идет речь в области идентификаторов внутри блока ** с **. Объявление и инициализация локального псевдонима переменной приводит к аналогичному сокращению кода, но поддерживает явное разыменование. – 2012-06-07 07:51:09

3

Эта дискуссия также происходит в Javascipt.

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

1

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

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

+0

Это может ускорить процесс, если задействованы множественные уровни косвенности. Рассмотрим следующее: с aControl.parent.parent.parent do ... а затем несколько операций. Оператор WITH только один раз оценивает тройную обратную связь и использует эту ссылку несколько раз. – 2008-11-02 18:04:23

+1

Откуда вы знаете, что компилятор не делает этого без с? Некоторое время назад я делал некоторые базовые бенчмаркинга и не мог найти никакой разницы. – Blorgbeard 2008-11-03 02:30:01

+1

@MasonWheeler - Тот же результат может быть достигнут с помощью `GreatGrandParent: = aControl.parent.parent.parent;` и обращения к членам `GreatGrandParent`. Только он гораздо читабельнее, чем с `with`. – mg30rg 2014-10-31 09:43:02

11

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

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

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

+5

Согласовано. Это в основном единственное, что мне нравится в VB над Delphi. – 2008-11-02 18:00:55

+2

Это не работает с вложенными «с». (Тебе нравятся или нет.) – 2009-02-05 08:37:51

+0

@Ulrich: синтаксис VB * работает * с вложенными `with`:` With foo` ... `With .bar` .... – 2010-07-27 11:19:25

31

Одно раздражение при использовании с тем, что отладчик не может его обработать. Таким образом, отладка сложнее.

Большая проблема заключается в том, что читать код проще. Особенно, если оператор 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, но это действительно так. Компилятор предупредил бы вас, если бы вы не использовали это, но теперь этого не происходит.

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

1

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

3

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

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

2

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

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

С а, Ь, с, d делать {кроме того что они гораздо больше имен, просто укороченные здесь) начинают я: = хуг;
end;

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

i: = c.xyz;

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

8

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

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

Бывают случаи, когда это реальная проблема, и случаи, когда это не проблема (не связанные с проблемой случаи будут такими, как описано в вопросе, как «разумно»).

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

-1

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

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

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

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

В дальнейшем, Foo только вычисляется один раз:

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

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

foo.bar := 1; 
foo.bin := x; 
foo.box := 'abc'; 
1
... работать быстрее ...

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

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

3

Это прежде всего обслуживание выпуск

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

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

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

11

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

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

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

2

Вы можете комбинировать с операторами, так что вы в конечном итоге с

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

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

-1

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

6

В самом деле:

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 непосредственно, чтобы получить внутренние значения.