четверг, 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());
}

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

четверг, 18 июня 2015 г.

Небольшой трюк при работе с ConcurrentDictionary

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

Простая реализация некоторого провайдера с cache aside паттерном будет выглядеть примерно так:

public class CustomProvider
{
   
private readonly ConcurrentDictionary<string, OperationResult> _cache =
        new ConcurrentDictionary<string, OperationResult>
();

   
public OperationResult RunOperationOrGetFromCache(string
operationId)
    {
       
return _cache.GetOrAdd(operationId, id =>
RunLongRunningOperation(id));
    }

   
private OperationResult RunLongRunningOperation(string
operationId)
    {
       
// Running real long-running operation
        // ...
        Thread.Sleep(10
);
       
Console.WriteLine("Running long-running operation"
);
       
return OperationResult.Create(operationId);
    }
}

 

пятница, 12 июня 2015 г.

Синдром “придумано не нами”

Ваш код сегодняшний, коллега
Напоминает даунхил:
Среди деревьев и гов#@ща –
Велосипед и костыли ...
Народная мудрость

Пару лет назад я сделал небольшой цикл заметок о паттернах поведения: Технический долг, Синдром рефакторинга и Эффект второй системы. Пришло время обсудить еще один, наверное, самый известный и популярный паттерн поведения – синдром «Придумано не нами» (NIH, Not Invented Here).

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

У «забить на существующее и сделать по-своему» есть несколько причин.