Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[35.173.48.18] |
|
Сообщ.
#1
,
|
|
|
Перебор записей в TDataSet
Иногда при переборе записей из датасета хочется написать простой пропуск итерации: ... if DS.FieldByName('foo').IsNull then Continue; ... - ан нет. Необходимость строчки DS.Next держит нас в рамках цикла словно пудовая гиря. Приходится вставлять ветвления, громадные вложенные begin-end и прочее. Я уже не говорю о случае, когда DS.Next просто забываешь поставить. Предлагаю пару простеньких оберток, добавляющих синтаксического сахара к перебору записей. Способ 1. for-in для датасета (добавляем итератор). Для Дельфи начиная с XE (используются хелперы). {$IF CompilerVersion >= 22} // RAD_XE+ // реализация энумератора для TDataSet type // заглушка для подставки в условие цикла. Реально нигде не используется TDataSetLoop = record end; TDataSetEnum = class private FOwner: TDataSet; FListBegin: Boolean; // чтобы не пропустить первую запись function GetCurrent: TDataSetLoop; public constructor Create(aOwner: TDataSet); function MoveNext: Boolean; property Current: TDataSetLoop read GetCurrent; end; TDataSetEx = class helper for TDataSet // возвращает объект-enumerator для конструкции for..in function GetEnumerator: TDataSetEnum; end; { TDataSetEnum } constructor TDataSetEnum.Create(aOwner: TDataSet); begin inherited Create; FOwner := aOwner; FListBegin := True; FOwner.First; end; function TDataSetEnum.GetCurrent: TDataSetLoop; begin // ничего не делать end; { Принципы прохода сильно различаются у for-in и по датасету: у for-in - while MoveNext do ..., у датасета - do ...; MoveNext; while not Eof (причем Eof устанавливается только после MoveNext с последней записи). Поэтому для согласования начала цикла вводится флаг FListBegin (для первой итерации не двигать указатель записи) и дополнительная проверка на Eof после перехода на следующую запись } function TDataSetEnum.MoveNext: Boolean; begin Result := not FOwner.Eof; if Result then if not FListBegin then begin FOwner.Next; if FOwner.Eof then Result := False; end else FListBegin := False; end; { TDataSetEx } function TDataSetEx.GetEnumerator: TDataSetEnum; begin Result := TDataSetEnum.Create(Self); end; {$IFEND} Пример использования: var loop: TDataSetLoop; RecCount := 0; for loop in qCurr do begin if qCurr.FieldByName('notnull').IsNull then Break; if qCurr.FieldByName('foo').IsNull then Continue; if qCurr.FieldByName('bar').IsNull then Continue; ... Inc(RecCount); end; + Привычный вид цикла + Удобно для больших циклов - Надо объявлять переменную Способ 2. ForEach для датасета. Для Дельфи начиная с 2006 (используются лямбда-функции). Можно также реализовать в виде хелпера. {$IF CompilerVersion >= 18} // 2006+ type TDataSetForEachProc = reference to procedure (DS: TDataSet; var Continue: Boolean); procedure DataSetForEach(DS: TDataSet; ForEachProc: TDataSetForEachProc); var Continue: Boolean; begin Continue := True; DS.First; while not DS.Eof do begin ForEachProc(DS, Continue); if not Continue then Break; DS.Next; end; end; {$IFEND} Пример использования: RecCount := 0; DataSetForEach(qCurr, procedure(DS: TDataSet; var Continue: Boolean) begin if qCurr.FieldByName('notnull').IsNull then begin Continue := False; Exit; begin if DS.FieldByName('foo').IsNull then Exit; if DS.FieldByName('bar').IsNull then Exit; ... Inc(RecCount); end); + Никаких дополнительных переменных - Непривычно для Дельфийского взгляда - Не очень удобно для больших циклов upd. Исправлен баг в for-in реализации: последняя запись обрабатывалась дважды |