Здравствуйте,
уважаемые читатели моего блога. Эта статья посвящена мероприятию, которое мне
посчастливилось посетить − Kiev ALT.NET. Конференция была организована компанией Ciklum, за что
хотелось бы выразить огромною благодарность организаторам, поскольку все было
сделано на высшем уровне. Встреча проходила на 20-ом этаже, в очень просторном
помещении, в обстановке, которая чем-то напоминает домашнюю. Ниже приведена
картинка офиса, в котором проходила конференция.
Только
все пуфики были расположены полумесяцем вокруг проектора. С места, в котором
была встреча, открывается бесподобный вид на Киев. Я взял с блога фото панорамы, поскольку из-за дождя фотографии у меня получились невысокого качества.
Также
позитивным моментом была возможность выпить сок или чай/кофе, угоститься печеньем, чипсами и конфетами. После того, как все расположились, нам рассказали несколько
слов о докладах, которые будут представлены на встрече. В начале конференции также поступило предложение от организаторов продолжить обсуждения конференции за кружкой пива при желании и наличии дополнительных вопросов к докладчикам. По
семейным обстоятельствам я не смог остаться на этом мероприятии, но думаю, что
оно состоялось. Также была озвучена новость о том, что данную встречу планируют
сделать регулярной. Это не может не радовать, так как доклады, которые были
представлены на конференции, были профессионально подготовленные и интересные,
что немаловажно.
На встрече было представлено два доклада из запланированных трех. Не был представлен доклад "Canopy: F# DSL for Webdriver от Serhiy Kalinets", так как предыдущие доклады оказались настолько увлекательными, что заняли все время, выделенное на встречу. Первым выступил Аким Бойко (Akim Boyko) со своим докладом "FsCheck: Property-based testing for F# and C#". Ниже привел фото автора доклада.
На встрече было представлено два доклада из запланированных трех. Не был представлен доклад "Canopy: F# DSL for Webdriver от Serhiy Kalinets", так как предыдущие доклады оказались настолько увлекательными, что заняли все время, выделенное на встречу. Первым выступил Аким Бойко (Akim Boyko) со своим докладом "FsCheck: Property-based testing for F# and C#". Ниже привел фото автора доклада.
До этого мне приходилось слушать доклады этого автора, поэтому не было сомнений в
том, что материал будет интересен. Один из докладов этого автора я впервые услышал,
когда решил познакомиться с проектом Roslyn и нашел один из его докладов "Метапрограммирование в .Net: RoslynCTP". Все время на встрече я старался нотировать интересные
вещи, почерпнутые с докладов, и пока они держатся в голове, хотел бы
поделиться ими с вами.
Началом доклада было банальное ознакомление с библиотекой FsCheck, которая предназначена для случайного и автоматического тестирования, основанного на свойствах. Также нам кратко рассказали о выборках тестирования с помощью функций forAll, throws, within, trivial и atLeastOne. Записывал по памяти, так что могут быть опечатки. Также были кратко рассмотрены аналогичные механизмы тестирования для языка Scala – ScalaCheck и другие, которые, к сожалению, я не записал, понадеявшись на свою память. Забыл упомянуть тот факт, что, по сути, FsCheck работает с языком F#. Подружить его с языком C# можно, но очень проблематично. Одной из проблем, почему это составляет проблему, являются Generators – генераторы, которые генерируют входные данные для тестируемых методов.
Основное отличие от стандартных Unit тестов, к которым мы привыкли при использовании подхода с TDD или BDD, состоит в том, что FsCheck позволяет модифицировать входную последовательность по ходу выполнения. Также мы можем указать, сколько генераций нам необходимо выполнить для тестируемого метода. По умолчанию в FsCheck задано 100 итераций, что бывает очень мало, если нужно протестировать, например, наличие вершины в графе. Частично такую возможность в юнит-тестах, которую позволяет покрыть FsCheck, можно сделать с помощью TestCase + Contracts. При написании тестов для FsCheck существует такое понятие, как Shrinkers – это минимальный набор входных данных, на которых получается получить ошибку. Также отличие FsCheck от стандартных юнит-тестов – в том, что позволяют проанализировать полностью выполнения тестирование метода, в то время как юнит-тест свалится при первой же ошибке.
Основная проблема FsCheck для языка C# состоит в сложности написания генераторов. Написать генераторы для F# – процесс непростой, а для C# он намного сложнее. Во-первых, он слабо документирован, и синтаксис местами не понятен. Вторая проблема заключается в том, что все результаты тестирования выводятся в Output. Подружить с FsCheck с классическими юнит-тестами сложно. Хотя существует интеграция FsCheck с xUnit, если верить докладу, потому что отдельно я не разбирал эту тему. Поскольку единственный язык, который понимают юнит-тесты, об ошибке выполнения, есть бросание исключения, то с парсингом выходного результата, который нам дает FsCheck, есть множество проблем.
Иногда процесс написания последовательности для теста может быть намного сложнее, чем сам тест. Одним из плюсов, о которых говорил автор доклада , является то, что подход с FsCheck заставляет разработчика думать о своих тестах и коде в пределах контрактов. К сожалению, я с этим не согласен, поскольку думать терминами контрактов можно уже сейчас, и подружить контракты с юнит-тестами можно без проблем. Контракты, которые уже есть в .NET 4.0, могут служить неплохим дополнением к тестам. По поводу того, как можно к своему коду подходить с точки зрения контрактов, рекомендую статьи известного в Украине MVP Сергея Теплякова "Контракты vs Юнит тесты" и "Контракты, состояние и юнит-тесты". Я к контрактам отношусь довольно предвзято, но отрицать их полезность в подходе к разработке не могу. Пожалуй, с докладом о FsCheck закончу, остановившись на втором докладе, ради которого, признаться честно, я и хотел пойти на встречу. Этот доклад "C# Under the hood от Antya Dev " (Антон Молдован). Ниже привел фото автора.
Началом доклада было банальное ознакомление с библиотекой FsCheck, которая предназначена для случайного и автоматического тестирования, основанного на свойствах. Также нам кратко рассказали о выборках тестирования с помощью функций forAll, throws, within, trivial и atLeastOne. Записывал по памяти, так что могут быть опечатки. Также были кратко рассмотрены аналогичные механизмы тестирования для языка Scala – ScalaCheck и другие, которые, к сожалению, я не записал, понадеявшись на свою память. Забыл упомянуть тот факт, что, по сути, FsCheck работает с языком F#. Подружить его с языком C# можно, но очень проблематично. Одной из проблем, почему это составляет проблему, являются Generators – генераторы, которые генерируют входные данные для тестируемых методов.
Основное отличие от стандартных Unit тестов, к которым мы привыкли при использовании подхода с TDD или BDD, состоит в том, что FsCheck позволяет модифицировать входную последовательность по ходу выполнения. Также мы можем указать, сколько генераций нам необходимо выполнить для тестируемого метода. По умолчанию в FsCheck задано 100 итераций, что бывает очень мало, если нужно протестировать, например, наличие вершины в графе. Частично такую возможность в юнит-тестах, которую позволяет покрыть FsCheck, можно сделать с помощью TestCase + Contracts. При написании тестов для FsCheck существует такое понятие, как Shrinkers – это минимальный набор входных данных, на которых получается получить ошибку. Также отличие FsCheck от стандартных юнит-тестов – в том, что позволяют проанализировать полностью выполнения тестирование метода, в то время как юнит-тест свалится при первой же ошибке.
Основная проблема FsCheck для языка C# состоит в сложности написания генераторов. Написать генераторы для F# – процесс непростой, а для C# он намного сложнее. Во-первых, он слабо документирован, и синтаксис местами не понятен. Вторая проблема заключается в том, что все результаты тестирования выводятся в Output. Подружить с FsCheck с классическими юнит-тестами сложно. Хотя существует интеграция FsCheck с xUnit, если верить докладу, потому что отдельно я не разбирал эту тему. Поскольку единственный язык, который понимают юнит-тесты, об ошибке выполнения, есть бросание исключения, то с парсингом выходного результата, который нам дает FsCheck, есть множество проблем.
Иногда процесс написания последовательности для теста может быть намного сложнее, чем сам тест. Одним из плюсов, о которых говорил автор доклада , является то, что подход с FsCheck заставляет разработчика думать о своих тестах и коде в пределах контрактов. К сожалению, я с этим не согласен, поскольку думать терминами контрактов можно уже сейчас, и подружить контракты с юнит-тестами можно без проблем. Контракты, которые уже есть в .NET 4.0, могут служить неплохим дополнением к тестам. По поводу того, как можно к своему коду подходить с точки зрения контрактов, рекомендую статьи известного в Украине MVP Сергея Теплякова "Контракты vs Юнит тесты" и "Контракты, состояние и юнит-тесты". Я к контрактам отношусь довольно предвзято, но отрицать их полезность в подходе к разработке не могу. Пожалуй, с докладом о FsCheck закончу, остановившись на втором докладе, ради которого, признаться честно, я и хотел пойти на встречу. Этот доклад "C# Under the hood от Antya Dev " (Антон Молдован). Ниже привел фото автора.
Доклад
был сделан профессионально и интересно. Автор доклада часто употреблял много
жаргонных словечек из программистского лексикона, что придавало докладу особый
колорит. Материал затрагивал несколько интересных вещей, по которым я и пройдусь. Доклад состоял из пяти условных частей:
- switch;
- lifted operators;
- lambda expressions;
- async/await;
- dynamic.
Теперь
немного расскажу о том, что подразумевал под собой каждый из пунктов
выше. Что вы знаете об операторе switch? Если вы знаете, как работает оператор switch на уровне кода и
как он оптимизирует свои запросы, то эта часть для вас может быть неинтересной. Но, как оказалось, я заблуждался насчет некоторых моментов работы switch. Одним из таких
нюансов есть использование данного оператора для сравнения строк. Я всегда
думал, что для такого кода
string a = "f";
switch (a)
{
case "a":
Console.WriteLine("a"); break;
case "b":
Console.WriteLine("b"); break;
case "c":
Console.WriteLine("c"); break;
case "d":
Console.WriteLine("d"); break;
case "e":
Console.WriteLine("e"); break;
case "f":
Console.WriteLine("f"); break;
}
компилятором строится таблица переходов (хеш-таблица), и в данном контексте
switch работает быстро. Оказывается я заблуждался, на уровне Il кода происходит самое обычное сравнение строк,
если значений в операторе switch меньше 7 (актуально
для строк); если больше или
равно 7-ми case значениям, тогда уже строится хеш-таблица. Ниже приведен IL код, который подтверждает эти слова.
// Methods
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x23b0
// Code size 171 (0xab)
.maxstack 2
.entrypoint
.locals init (
[0] string a,
[1] string CS$0$0000
)
IL_0000: ldstr "f"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: dup
IL_0008: stloc.1
IL_0009: brfalse IL_00a4
IL_000e: ldloc.1
IL_000f: ldstr "a"
IL_0014: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0019: brtrue.s IL_005e
IL_001b: ldloc.1
IL_001c: ldstr "b"
IL_0021: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0026: brtrue.s IL_006a
IL_0028: ldloc.1
IL_0029: ldstr "c"
IL_002e: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0033: brtrue.s IL_0076
IL_0035: ldloc.1
IL_0036: ldstr "d"
IL_003b: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0040: brtrue.s IL_0082
IL_0042: ldloc.1
IL_0043: ldstr "e"
IL_0048: call bool [mscorlib]System.String::op_Equality(string, string)
IL_004d: brtrue.s IL_008e
IL_004f: ldloc.1
IL_0050: ldstr "f"
IL_0055: call bool [mscorlib]System.String::op_Equality(string, string)
IL_005a: brtrue.s IL_009a
IL_005c: br.s IL_00a4
IL_005e: ldstr "a"
IL_0063: call void [mscorlib]System.Console::WriteLine(string)
IL_0068: br.s IL_00a4
IL_006a: ldstr "b"
IL_006f: call void [mscorlib]System.Console::WriteLine(string)
IL_0074: br.s IL_00a4
IL_0076: ldstr "c"
IL_007b: call void [mscorlib]System.Console::WriteLine(string)
IL_0080: br.s IL_00a4
IL_0082: ldstr "d"
IL_0087: call void [mscorlib]System.Console::WriteLine(string)
IL_008c: br.s IL_00a4
IL_008e: ldstr "e"
IL_0093: call void [mscorlib]System.Console::WriteLine(string)
IL_0098: br.s IL_00a4
IL_009a: ldstr "f"
IL_009f: call void [mscorlib]System.Console::WriteLine(string)
IL_00a4: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_00a9: pop
IL_00aa: ret
} // end of method Program::Ma
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x23b0
// Code size 171 (0xab)
.maxstack 2
.entrypoint
.locals init (
[0] string a,
[1] string CS$0$0000
)
IL_0000: ldstr "f"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: dup
IL_0008: stloc.1
IL_0009: brfalse IL_00a4
IL_000e: ldloc.1
IL_000f: ldstr "a"
IL_0014: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0019: brtrue.s IL_005e
IL_001b: ldloc.1
IL_001c: ldstr "b"
IL_0021: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0026: brtrue.s IL_006a
IL_0028: ldloc.1
IL_0029: ldstr "c"
IL_002e: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0033: brtrue.s IL_0076
IL_0035: ldloc.1
IL_0036: ldstr "d"
IL_003b: call bool [mscorlib]System.String::op_Equality(string, string)
IL_0040: brtrue.s IL_0082
IL_0042: ldloc.1
IL_0043: ldstr "e"
IL_0048: call bool [mscorlib]System.String::op_Equality(string, string)
IL_004d: brtrue.s IL_008e
IL_004f: ldloc.1
IL_0050: ldstr "f"
IL_0055: call bool [mscorlib]System.String::op_Equality(string, string)
IL_005a: brtrue.s IL_009a
IL_005c: br.s IL_00a4
IL_005e: ldstr "a"
IL_0063: call void [mscorlib]System.Console::WriteLine(string)
IL_0068: br.s IL_00a4
IL_006a: ldstr "b"
IL_006f: call void [mscorlib]System.Console::WriteLine(string)
IL_0074: br.s IL_00a4
IL_0076: ldstr "c"
IL_007b: call void [mscorlib]System.Console::WriteLine(string)
IL_0080: br.s IL_00a4
IL_0082: ldstr "d"
IL_0087: call void [mscorlib]System.Console::WriteLine(string)
IL_008c: br.s IL_00a4
IL_008e: ldstr "e"
IL_0093: call void [mscorlib]System.Console::WriteLine(string)
IL_0098: br.s IL_00a4
IL_009a: ldstr "f"
IL_009f: call void [mscorlib]System.Console::WriteLine(string)
IL_00a4: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
IL_00a9: pop
IL_00aa: ret
} // end of method Program::Ma
Пожалуй,
перейдем ко второй теме и поговорим о lifted operators. Это операторы, которые позволяют работать безопасно с nullable типами данных. Например, валидной является запись
public static int?
Result(int? a, int? b)
{
return a + b;
}
И
другие нюансы, связанные с nullable типами.
Следующая
тема Lambda Expression затронула
удивительный мир монад. Автор доклада почему-то затронул только ту часть лямбда-выражений, которая строится вокруг точечной нотации. К сожалению, в этой теме
для себя я не узнал ничего нового, правда, причина этого была в том, что я уже
знаком с монадами со статей Ерика Липперта в его блоге Fabulous Adventures In Coding.
Правда, автор рассказывал о монадах довольно интересно. Эта
тема была очень беглой и, по сути, автор доклада ознакомил слушателей только с
монадой MayBe и
показал пример ее использования. Это тоже очень неплохо из-за
ограниченности по времени доклада.
Следующей
интересной темой лично для меня было объяснение того, как работает async/await в C#. Также был создал искусственный пример, который
эмулировал работу с async/await,
как это делает компилятор. Понравился тот момент, что автор указал на
популярную ошибку при использовании async/await: использование явно метода WaitAll или Result, что, по сути,
вешало основной поток. После того как был продемонстрирован пример, я понял, почему это происходило.
Последняя
тема, которая вызвала дискуссию в зале, была тема, связанная с dynamic. Автор доклада рассказал невзначай о своем фреймворке, над которым он работает (KingAOP).
Сама тема не касалась этого фреймфорка и он был затронут вскользь, но я
специально самостоятельно позже посмотрел, на чем основывается данный
фреймворк (в основе лежит dynamic и IDynamicMetaObjectProvider и фреймфорк PostSharp) и не смог придумать ему применения, наверное, больше
из-за своей нелюбви к PostSharp. Сам же доклад о dynamic очень понравился,
поскольку рассказал о том, как работает данный тип изнутри IL. Но он вызвал также множество вопросов и дискуссий по
поводу DLR, которая
была затронута в рамках этого доклада. Лично я выразился о том, что DLR на данный момент не
доделана до конца, и что dynamic (ExpandoObject/Dynamic) – лишь верхушка айсберга, а другие языки, как IronPython и IronRuby, не прижились и, по
сути, являются недоделанными частями DLR. Также очень тормозит взаимодействие между C# кодом и IronPython через ScriptEngine. Также на взаимодействие накладывается куча ограничений.
В целом, тема была позитивной и приоткрыла некоторые занавесы мира C#.
Надеюсь,
этот краткий обзор позволит вам оценить пользу тем, которые были озвучены
на встрече, и на следующую встречу будет еще больше желающих разработчиков.
No comments:
Post a Comment