Страницы

среда, 16 сентября 2015 г.

Изменение курсора и автоматическое восстановление при выходе из метода

Не знаю как у вас, а у меня коде (vcl приложение) полным-полно таких конструкций:

var
  tmpOldCursor: TCursor;
begin  
  tmpOldCursor := Screen.Cursor;
  try
    Screen.Cursor := crHourglass;
    // код который может работать относительно долго
    // например, выполнять запрос в БД
  finally
    Screen.Cursor := tmpOldCursor;
  end;
end;

И мне это надоело. По двум причинам:

  1. увеличение размера модулей - по 8 строк кода на каждый такой случай
  2. разбухание секции uses, ведь чтобы это работало нужно в каждый модуль работающий с курсором добавить uses Forms, Controls;

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

begin  
  TLazyCursorHelper.ChangeToHourglass;
  // код который может работать относительно долго
  // например, выполнять запрос в БД
end; // а здесь курсор должен восстановиться

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

update: в комментариях дали ссылку на более универсальное и элегантное решение от Barry Kelly (который долгое время занимался разработкой компилятора dcc32 в Borland/Embarcadero). Barry уверяет, что такой способ весьма надёжен.

Реализация TLazyCursorHelper:

11 комментариев:

  1. У Barry Kelly более универсальный способ описан:
    http://blog.barrkel.com/2010/01/one-liner-raii-in-delphi.html

    ОтветитьУдалить
    Ответы
    1. Да, спасибо! У Барри действительно очень изящный способ описан.

      Удалить
    2. Не могли бы вы пожалуйста выложить пример с изменением курсора основанный на методе барри?

      Удалить
    3. Анонимный, примерно как-то так: https://gist.github.com/tdelphi/99c4c847fd91da59ea97

      Удалить
  2. Примерно этот же способ нашёл тут: http://habrahabr.ru/post/147575
    Для себя видоизменил его до передачи курсора в конструктор.
    Теперь же, благодаря, комментарию выше, видоизменил снова, убрав ненужный интерфейс и оставив снаружи только функцию, возвращающую IInterface.

    ОтветитьУдалить
    Ответы
    1. На хабре imho довольно кривенькая реализация.

      А по поводу оставления снаружи только функции. В моём случае - функций несколько, по одной для каждого типа курсора(чтобы не добавлять uses Controls только ради курсорной константы). А в последнее время я стараюсь не оставлять глобальных функций, и все функции группировать (либо внутри класса как class methods) либо внутри record-a - как статичные методы. Мне проще иметь одна точку доступа (класс или record) ко всем моим функциям работающим с курсором, чем несколько глобальных функций. К тому же это меньше засоряет Autocomplete в редакторе.

      Удалить
    2. bq. не добавлять uses Controls только ради курсорной константы

      Это правильно :-)

      Удалить
    3. @Александр - недавно начал использовать DevExpress. На фоне того количества "используемых юнитов", которое добавляют DX компоненты (кладёшь на форму 1, получаешь + 7 uses), эти Controls оказались такими мелочами.

      Удалить
  3. Можно же лямбды (анонимные функции) использовать.

    http://programmingmindstream.blogspot.ru/2015/09/blog-post_4.html

    ОтветитьУдалить
    Ответы
    1. Можно, можно. У Барри в журнале (ссылкка в первом комментарии) как раз на лямбдах описан способ.
      Я просто (по старой привычке), на публику пишу код работающий в версиях Delphi 6 и моложе.

      Удалить
    2. Понятно. Ну я на самом деле так и подумал. Да и с лямбдами - многословнее получается.

      Я вот что ещё вспомнил - http://programmingmindstream.blogspot.ru/2015/09/1163.html

      Может полезно будет.

      Удалить