четверг, 13 сентября 2007 г.

День Программиста

Друзья и коллеги! Сегодня 256-й день года, который традиционно считается Днем Программиста. С чем всех и поздравляю!

воскресенье, 9 сентября 2007 г.

Каждый охотник желает знать, где сидит фазан

Эта знакомая с детства фраза – не что иное, как пример мнемонической фразы. Напомню, что мнемоника — это совокупность приемов, имеющих целью облегчить запоминание возможно большего числа сведений, фактов и т.п. путем образования искусственных ассоциаций. Во фразе, которая приведена в заголовке статьи, первая буква каждого слова помогает вспомнить цвет. А сегодня мы поговорим об использовании мнемоники в Visual Studio. В этой среде разработки, как в типичном Windows-приложении, есть два основных способа вызова команд с клавиатуры: горячие клавиши и мнемоника.

С горячими клавишами все понятно. В главном меню они высвечиваются справа от текста пункта меню, а для команд панели инструментов высвечиваются во всплывающей подсказке, если установлена соответствующая опция (Tools -> Customize -> Show ScreenTips on toolbars). Число горячих клавиш ограничено – они назначены только для самых популярных команд.
Другой, менее популярный, но более мощный способ быстрого вызова команд, – использование мнемоники. Мнемоника позволяет при помощи клавиши Alt и выделенных символов вызвать практически любую команду главного меню приложения. Их проще запоминать, поскольку, как правило, они назначаются по первым буквам пунктов меню и при нажатой клавише Alt подчеркиваются. Вот некоторые часто используемые мной мнемонические команды студии:

  • Alt + W + L — закрыть все открытые окна-закладки (Window -> Close All Documents)
  • Alt + F + T — закрыть солюшен (File -> Close Solution)
  • Alt + B + U — скомпилировать текущий проект из солюшена (Build -> Build …)
  • Alt + D + X — вызвать окно Exceptions (Debug -> Exceptions)
  • Alt + P + R — добавить ссылку на сборку(Project -> Add Reference)
  • Alt + P + C — добавить новый класс в проект (Project -> Add Class)
  • Alt + T + O — открыть окно настроек студии «Options»(Tools -> Options)

Мнемоникой удобно пользоваться и в других Windows-приложениях (сочетания приведены для английской версии Windows):

  • Alt + D — в проводнике и Internet Explorer перемещает курсор ввода в поле адреса.
  • Alt + V + L — в проводнике переключает в режим просмотра в виде списка(View -> List)
  • Alt + V + D — в проводнике переключает в режим просмотра в виде таблицы(View -> Details)
  • Alt + T + O — в Internet Explorer открывает окно «Internet Options» (Tools -> Internet Options)

Между прочим, мнемонические команды работают и в 7-й версии Internet Explorer, несмотря на то, что у него меню по умолчанию скрыто.Так что не забывайте назначать мнемонику на пунктах меню, хотя я, к сожалению, сам постоянно забываю это делать :-).

пятница, 7 сентября 2007 г.

Техника быстрой локализации ошибок при отладке

Процесс отладки упрощенно состоит из нескольких этапов. Сперва необходимо добиться повторяемости ошибки. Как правило, это делает тот, кто находит и/или регистрирует баг, но в наиболее сложных ситуациях (когда ошибка возникает нерегулярно или ее зависимость от внешних факторов не очевидна) это приходится делать самим.

Далее необходимо локализовать баг в программном коде. Чтобы не читать метры кода, лучше максимально сократить тот диапазон, который в дальнейшем придется изучать, чтобы понять, какой фрагмент кода приводит к ошибке. Затем надо понять причину проблемы и, наконец, исправить.

По моему опыту, локализация ошибки – наиболее трудоемкий этап в 95% случаев. Если ошибка стала проявляться сравнительно недавно (то есть, можно указать дату и/или номер билда, когда ошибки точно не было и дату и/или номер билда, когда ошибка точно проявляется), могу предложить простую, но эффективную технику, которой сам успешно пользуюсь. Способ локализации ошибки основан на алгоритме двоичного поиска и состоит из двух последовательных действий:

  1. Локализация билда системы, когда баг стал проявляться.
  2. Локализация фрагмента кода, где проявляется баг.

А теперь – подробней.

Локализуем билд системы, когда баг стал проявляться

Фиксируем граничные билды: проверяем тот билд, где в мы уверены, что бага нет, затем проверяем билд, где мы уверены, что баг проявляется. Может показаться, что эти проверки излишни (мы ведь и так уверены), однако это не так. Неверное определение граничных билдов приведет к значительно большей потери времени и, как показывает практика, случаются не так уж и редко, так что я рекомендую не полениться и выполнить их! Границы зафиксированы и теперь ищем билд, который находится посередине, и проверяем его. Тут возможны 2 варианта. Если серединный билд содержит баг, значит он появился раньше чем мы предполагали. Если серединный билд чист перед законом, то баг пробрался в систему позже. Фиксируем новые граничные билды. Этот нехитрый процесс повторяем до тех пор, пока не найдем два последовательных билда: первый чистый, следующий за ним — с багом.

Локализуем фрагмент кода, где проявляется баг

По системе контроля версий сливаем исходный код билда без бага и фиксируем ВСЕ изменения между билдами. Тут желательно еще раз проверить, что баг действительно проявляется. Далее вносим в чистый билд половину всех изменений. Определять половину можно на глаз (аптекарская точность здесь не важна), главное, чтобы код компилировался, и мы могли проверить повторяемость бага. Снова смотрим поведение программы. Если баг стал проявляться, то уменьшаем порцию изменений вдвое. Если нет, то от оставшейся части изменений также находим половину и добавляем ее к системе. Этот процесс повторяется до тех пор, пока не обнаружатся те строки кода, добавление которых приводит к появлению бага.

Надеюсь, описал понятно. Если есть вопросы и/или требуются где-то пояснения, не стесняйтесь, спрашивайте! Техника проста, по сути – механическая работа, не требующая большого интеллекта, который следует поберечь для других случаев :).


понедельник, 3 сентября 2007 г.

Ловкость рук и никакого мошенничества

Заметил, так же, что многие даже и не подозревают, что если Tab позволяет перемещаться по контролам в прямом порядке: слева на права и сверху в низ, то комбинация Shift + Tab перемещает фокус в обратном направлении.

Еще комбинация из разряда особо не известных J. Ctrl + C на стандартных окнах сообщений (MessageBox) позволяет скопировать в буфер в текстовом виде содержимое этого окна: заголовок окна, сообщение, надписи на кнопках.

воскресенье, 2 сентября 2007 г.

Нетрадиционное использование using и IDisposable

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

private void button1_Click(object sender, EventArgs e)
{
Cursor oldCursor = Cursor.Current;
try
{
Cursor.Current = Cursors.WaitCursor;
// do something time consuming
DoSomethingTimeConsuming();
}
finally
{
Cursor.Current = oldCursor;
}
}

Хочу обратить внимание, что конструкция try finally необходима, что бы в случае необработанного исключения в DoSomethingTimeConsuming все равно восстановить курсор. Однако у данного обработчика есть несколько недостатков:

  • конструкция достаточно объемна
  • необходимость блока try finally не очевидна и поэтому блок можно забыть.
  • данный код приходится дублировать для каждого подобного обработчика.

У меня есть небольшой трюк, который выполняет ту же работу но не содержит выше перечисленные недостатки. Новый вариант обработчика будет выглядеть следующим образом:

private void button2_Click(object sender, EventArgs e)
{
using (WaitCursor wch = new WaitCursor())
{
// do something time consuming
DoSomethingTimeConsuming();
}
}

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

Код для класса WaitCursor:
internal class WaitCursor: IDisposable
{
private Cursor m_OldCursor;

internal WaitCursor()
{
m_OldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
}

public void Dispose()
{
Cursor.Current = m_OldCursor;
}
}

Использование using приводит к тому что блок try finally будет сгенерирован автоматически. Так же автоматически конструкцией using будет вызван метод WaitCursor. Dispose, который и восстановит курсор.
Хочу отметить, что подобный механизм можно использовать не только для управления курсором.