четверг, 22 октября 2015 г.

Пишем простой анализатор с помощью Roslyn

С выходом новой версии студии (VS2015) у каждого из нас появилась возможность почувствовать себя причастным к разработке инструментов для разработчиков. Камрады из команд компиляторов C#/VB проделали отличную работу по «выставлению» внутренностей компилятора наружу, что позволяет теперь писать свои собственные анализаторы кода, затрачивая вполне разумные на это силы.

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

Для чего нужны свои собственные анализаторы?

Вопрос вполне разумный. Есть «реактивные мозги», есть DevExpress, есть же мелкомягкие товарищи из DevDiv-а, которые пилят инструменты для разработчиков. Зачем мне разбираться со всякими иммутабельными синтаксическими деревьями и control flow анализом? Это довольно весело, но разве этого достаточно, чтобы тратить на это свое ценное время?

Любой инструмент общего назначения рассчитан на решение наиболее типичных задач. Достаточно открыть список анализаторов Решарпера, чтобы понять, о чем идет речь. Такие анализаторы отлично справиться с поиском недостижимого кода или с предупреждением о неправильной реализации синглтона, но он не «подскажет» о правилах специфичных для вашего проекта и/или библиотеки.

Например, вы можете захотеть реагировать более жестко на некорректное логгирование исключений (детектить и «бить по пальцам», если вашему методу логирования передается ex.Message, а не ex.ToString()), или же это может быть кастомное правило, запрещающее использовать LINQ в определенных сборках во избежание потери производительности. Если в вашей команде есть правило или набор правил, которому должны следовать все члены команды, но которое нельзя выразить в виде правил FxCop/StyleCop. Все эти задачи отлично будут решаться с помощью самописных анализаторов.

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

Элегантная реализация «слабых событий»

Камрад @v2_matveev указал на прекрасную реализацию слабых событий в коде Roslyn-а.

Проблема стара как мир: подписка на события долгоживущего объекта со стороны короткоживущего продлевает время жизни последнего. Сложно? Может быть. Но столкнуться с проблемой довольно легко: если у вас есть синглтон с событиями, поздравляю, в вашем приложении есть эта проблема и связанные с ней утечки памяти. Поскольку событие содержит корневую ссылку на хендлер, то любой подписчик будет жить до тех пор, пока он не отпишется от события или пока поставщик событий не умрет (что в случае синглтона произойдет лишь при выгрузке домена или окончания работы приложения).

Решается эта проблема разными способами. Самый правильный способ – не использовать события в синглтонах. Второй способ – отписываться от событий вовремя. Третий способ – хипстерский, воспользоваться той или иной реализацией Weak Event Pattern-а, например, с помощью WeakEventManager или чего-то подобного. Но раз уж зашла речь за слыбые события, то их можно реализовать по разному и далее показана самая изящная реализация.

Вначале демонстрируем проблему: добавляем синглтон с событиями и короткоживущий объект, который на них подписывается:

четверг, 13 августа 2015 г.

Начать ли использовать контракты?

Тут народ на rsdn-е интересуется, а стоит ли пользоваться Code Contracts,  вопрос интересный. Ответ такой вопрос звучит обычно так:“it depends”, но я могу помочь понять, от чего и что depends.

Краткий экскурс в историю контрактов

Сама идея контрактного программирования появилась в 80-х годах в голове Бертрана Мейера и основывалась на матанских выкладках Хоара и других умных дядек.

Заключалась она в следующем: программа сама по себе не является корректной или не корректной, это понятие применимо лишь к паре – (программа, ожидаемое поведение). А поскольку ожидаемое поведение обычно является совершенно непонятным делом, то хочется иметь возможность в кратком и implementation-agnostic виде выразить это самое поведение. Причем сделать это как можно ближе к реализации этого самого поведения.

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

четверг, 6 августа 2015 г.

Обзор возможностей C# 6.0

О новых возможностях языка C# написано чуть более, чем достаточно, но мне тоже хочется внести свою лепту. Я тут пару недель хакатонил и в новом проекте активно использовался C# 6.0, поэтому появился дополнительный опыт, которым можно уже поделиться.

Итак, ниже представлены фичи языка C# 6.0 в порядке их полезности для меня на данный момент времени.

String Interpolation

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

class UserNotFoundException : Exception
{
   
public UserNotFoundException(string
userId)
        :
base($"User '{userId}' was not found!")
    { }
}

вторник, 21 июля 2015 г.

Code Contracts в VS2015

В последние несколько недель я активно занимался доработкой Code Contracts, исправлением некоторых неприятных ошибок и добавлением поддержки VS2015. А поскольку VS2015 только что увидела свет, то подобная поддержка будет весьма кстати. Теперь обо всем об этом по порядку, да еще и с рядом технических подробностей.

Итак, первое, что нужно знать о Code Contracts, что эта штука жива. Код лежит в открытом доступе на гитхабе (https://github.com/Microsoft/CodeContracts) и есть ряд людей, которые активно занимаются наведением там порядка. Я являюсь owner-ом репозитория, но занимаюсь этим в свое свободное время. Помимо меня есть еще несколько человек, которые наводят порядок в Code Contracts Editor Extensions (@sharwell) и в некоторых других областях.

Code Contracts можно разделить на несколько составляющих:

  • ccrewrite – тул, который занимается «переписыванием» IL-а, выдиранием утверждений (Contract.Requires/Ensures/Assert/Assume/if-throw) и заменой их на нужные вызовы методов контрактов, в зависимости от конфигурации.
  • cccheck - тул, который занимается статическим анализом и формальным доказательством во время компиляции, что программа является корректной.
  • Code Contracts Editor Extensions – расширение к VS, которое позволяет «видеть» контракты прямо в IDE.

Есть еще ряд тулов, например, для генерации документации, а также плагин к ReSharper, который упрощает добавление предусловий/постусловий и показывает ошибки ccrewrite прямо в IDE.

Я занимаюсь двумя составляющими – ccrewrite и плагином, но сейчас хочу остановиться именно на ccrewrite и на сложностях, с которыми я столкнуться при добавлении поддержки VS2015.

вторник, 7 июля 2015 г.

Идиома ForEachAsync

Давайте продолжим рассматривать приемы, которые будут полезными при работе с TPL.

В прошлый раз мы рассмотрели идиому, которая позволяет обрабатывать результаты в порядке окончания работы задач, а не в порядке их запуска. Но там был пропущен один интересный момент. Вот, например, у нас есть все тот же сервис погоды и желание получить результаты по всем городам как можно быстрее. Означает ли это, что можно взять все города мира и послать одновременно тысячи запросов? Сервис погоды может посчитать, что клиент сошел с ума и попробует затроттлить (throttle) запросы, превышающие определенный лимит (кстати, этот самый троттлинг – это один большой pain in the ass для всех облачных сервисов, причем как для авторов сервисов, так и для клиентов этих самых сервисов).

Так вот, нам нужно как-то ограничить число таких запросов или каких-то других асинхронных операций.

Вообще, с ограничением числа задач есть одна небольшая беда. В случае CPU Intensive операций (числодробилки, операции, которые нагружают CPU/GPU) есть простая эвристика – число работающих задач должно быть ограничено числом вычислительных устройств. Но, в случае с IO Intensive операциями таких ограничений нет. Более того, нет и встроенных инструментов для контроля за числом таких операций.

воскресенье, 28 июня 2015 г.

Идиома Process Tasks By Completion

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

Можно подойти к этой задаче в лоб. Крутим цикл, запускаем таски, обрабатываем результаты по одному:

private Task<Weather> GetWeatherForAsync(string city)
{
   
Console.WriteLine("[{1}]: Getting the weather for '{0}'"
, city,
       
DateTime.Now.
ToLongTimeString());
   
return WeatherService.
GetWeatherAsync(city);
}


[
Test]
public async Task
ProcessOneByOneNaive()
{
   
var cities = new List<string> { "Moscow", "Seattle", "New York"
};

   
var tasks =
        from city in
cities
       
select new { City = city, WeatherTask =
GetWeatherForAsync(city) };

   
foreach (var entry in
tasks)
    {
       
var wheather = await entry.
WeatherTask;

        ProcessWeather(entry
.
City, wheather);
    }
}

private void ProcessWeather(string city, Weather
weather)
{
   
Console.WriteLine("[{2}]: Processing weather for '{0}': '{1}'"
, city, weather,
       
DateTime.Now.ToLongTimeString());
}

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