вторник, 22 октября 2013 г.

Вредные советы от ReSharper


Установил ReSharper и был откровенно удивлен некоторыми рекомендациями его Code Inspection...

Использование readonly не по назначению


Если только объявлено поле (field), значение которому не присваивается нигде, кроме конструктора или прямой инициализацией, ReSharper будет настойчиво уговаривать сделать его readonly. Это вполне понятно и объяснимо, когда это поле представляет из себя простой (или немодифицируемый) тип данных вроде строки или целого числа. Но когда ReSharper предлагает делать readonly коллекции, которые модифицируются во время жизни класса, это напрягает. Это напоминает константные shared pointer в C++. Вроде бы нужно делать все, что явно не меняется константным, но только когда это соответствует ответственности класса, а не только какой-то формальной семантике.

Использование var вместо явного указания типа


Здесь [...] авторы защищают это предложение. Даже microsoft не советуют применять var для неанонимных типов, но у JetBrains "свое мнение".
Якобы делает код "чище". По моему это просто еще один вид отказа от выбора имени для сущности. Самого кода, естественно, будет меньше. Но пострадает читабельность. При явном указании типа сразу понятно, с чем дальше мы будем иметь дело. А при использовании var остается либо догадываться, либо лезть в объявление метода, который вернул нам этот класс. Якобы это приводит к лучшему API. С чего вдруг? Скорее наоборот, разработчики будут вставлять имена возвращаемых типов в имена методов. Якобы убирает шум из кода. Ну какой шум? Наоборот, убирает сигнал, то есть важную информацию. Единственное, с чем можно согласиться, так это с тем, что использование var требует явно инициализировать переменные при объявлении.

четверг, 10 октября 2013 г.

Бесконечная рекурсия сводит Microsoft unit testing framework с ума

Совершенно случайно наткнулся на странное поведение юнит-тестов Microsoft. Нажимаю запуск всех тестов, проходит несколько секунд... И возвращаюсь в исходное состояние. Тестов как будто и не запускалось...Никаких ошибок, исключений, предупреждений, ничего... Некоторые тесты по отдельности запускались и успешно отрабатывали, а некоторые нет...
При отладке одного из тестов, вводящих Visual Studio в ступор, я увидел-таки заветную ошибку "An unhandled exception of type 'System.StackOverflowException' occurred". Получается, что при ошибке переполнения стека (например, из-за бесконечной рекурсии) простой запуск юнит-тестов не возвращает ошибку, а исключение "проглатывается" системой запуска тестов...
Например, такой код приведет к подобному поведению:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace RecursionUnitTest
{
    public class Recurse
    {
        public int Run()
        {
            return Run();
        }
    }

    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestRecursion()
        {
            Recurse rec = new Recurse();

            Assert.AreEqual(0, rec.Run());
        }
    }
}

пятница, 20 сентября 2013 г.

Сравнение коллекций/массивов в юнит-тестах Microsoft

Если вы используете фреймворк для юнит тестов, встроенный в Visual Studio, сравнение полученного результата с ожидаемым обычно производите используя класс Assert.
Но, если попробовать сравнить значения в коллекции или массиве (например, int[]) Assert.AreEqual() будет возвращать false для разных экземпляров массивов, даже если содержимое массивов одинаково.

Например:
[TestMethod]
public void TestArraysEquality()
{
  int[] array1 = new int[] { 1, 2, 3 };

  Assert.AreEqual(new int[] { 1, 2, 3 }, array1);
}

В результате тест завершается с ошибкой Assert.AreEqual failed.
Причина в том, что с массивом Assert.AreEqual проверяет равенство ссылок. Для простого примера, наподобие приведенного, можно просто написать три ассерта, по одному на каждый элемент... Но это решение на любителя. Или можно воспользоваться классом CollectionAssert, который пройдет через весь массив и проверит каждый элемент на равенство.
[TestMethod]
public void TestArraysEquality()
{
  int[] array1 = new int[] { 1, 2, 3 };

  CollectionAssert.AreEqual(new int[] { 1, 2, 3 }, array1);
}

Теперь тест завершится успешно.
К сожалению. CollectionAssert не обходит внутренние коллекции, и, если сравнить коллекцию коллекций, например, List<<int>>, то CollectionAssert.AreEqual все равно завершится ошибкой.

среда, 14 августа 2013 г.

NAT на Hyper-V под Windows 8

Все началось с того, что я захотел, чтобы на виртуальных машинах Hyper-V был работал интернет. При выборе сетевого адаптера для виртуальной машины доступно три варианта: private, internal и external. Private - сеть существует только для виртуальных машин на этом хосте, взаимодействия с внешним миром нет. External - создается мост (bridge) между сетевой картой хоста и виртуальной машиной. При этом интернет на хосте может не работать (у меня не работал). Internal - сеть существует между несколькими виртуальными машинами и хостом. NAT, то есть возможность взаимодействовать с внешним миром и виртуальным машинам и хосту, отсутствует. Но его можно сделать, используя средства Windows для совместного использования соединения с интернет (internet connection sharing). Для этого нужно будет выполнить три основных шага. К сожалению, после каждого шага лучше перезагрузиться, иначе вы рискуете не найти в настройках созданных виртуальных свитчей, адаптеров и соединений. Или просто ничего не будет работать...

1. Создаем Internal Virtual Switch, перезагружаемся

2. Расшариваем интернет-соединение на эту сетку, перезагружаемся

3. Подключаем созданный свитч к виртуальным машинам

П.С. Hyper-V, установленный на машину, забирает аппаратные виртуализационые возможности себе, так что другие гипервизоры (например, VirtualBox) будут работать в программном режиме виртуализации.

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

понедельник, 15 июля 2013 г.

Возможности C++, которые лучше не использовать

Image courtesy of Idea go / FreeDigitalPhotos.net

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

Шаблоны (templates)

Без минимального использования шаблонов в C++ не обойтись. Но стоит увлечься их использованием, компиляция программы замедлиться в разы, а найти кто именно, что именно и у кого позвал, становится практически невозможно. Особо стоит упомянуть бинды (boost::bind, tr1::bind и т.п.), при использовании которых вызов в функции превращается в нечитабельную и неотлаживаемую портянку:
std::tr1::bind(
  &SomeThing::Process,
  std::tr1::cref(specificThing),
  std::tr1::placeholders::_1,
  std::tr1::cref(cancelState),
  std::tr1::ref(accumulatingProgress))

А уж какие невнятные ошибки выводят компиляторы, когда не находит подходящей функции для бинда (например, когда список аргументов слегка изменился) не компенсирует даже многоэкранный размер этих ошибок.

Лямбда-выражения

Какой соблазн, описать какое-то действие, не думая над его названием и сразу исполнить. Осторожно! Это сыграет злую шутку с теми, кому придется поддерживать код. Ведь названия сущностей и действий - это и есть то, что помогает понять, что именно делает программа.

Макросы

В отличии от предыдущих двух, макрос - самый старый "друг" программиста, пришедший еще со времен чистого С. Но их использование чревато засорением глобального пространства имен, неудобством отладки и решения проблем компиляции. И самое неприятное, что макросы ведут себя совсем иначе, чем функции со своими аргументами, и в результате разворачивания макроса могут выполняться совсем не те операции, что были задуманы.

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

воскресенье, 14 июля 2013 г.

Предприниматели и наемные работники

Image courtesy of jscreationzs / FreeDigitalPhotos.net

Предприниматели нанимают работников в компании, которые они организуют. Но можно ли сказать, что предприниматель и наемный работник - это разные люди? Разный уровень ответственности, разные навыки, разный взгляд на жизнь... Но это теория - а что же на практике?

На практике и у предпринимателя и у наемного работника есть клиенты. Клиенты предпринимателя - это клиенты его компаний, клиенты наемных работников - основной работодатель, а также другие люди и компании, от которых он получает прибыль.

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

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

Получается, что разница между наемным работником и предпринимателем - это лишь их мировоззрение. То, как они сами себя позиционируют.