вторник, 3 мая 2011 г.

C# и синтаксический сахар

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

DictionaryAssert.AreMultiEquivalent(
new Dictionary<long, IList<long>>()
{
{
ConstantParameterIds.BookFormat,
new List<long>() {ConstantBookFormatValueIds.Paperback}
}
},
valuesDictionary);

Очень много лишних символов, не находите? На 5 минут представлю себя в роли архитектора C# и немного пофантазирую.

Сначала добавим вывод generic-типов для конструкторов:

DictionaryAssert.AreMultiEquivalent(
new Dictionary()
{
{
ConstantParameterIds.BookFormat,
new List() {ConstantBookFormatValueIds.Paperback}
}
},
valuesDictionary);

Я уже недавно сетовал на отсутсвие этой фичи в C#, и упоминал статическую фабрику в качестве workaround'а (new Tuple(5, 5) vs. Tuple.Create(5, 5)). Однако этот workaround не распространяется на случай использования инициализации через фигурные скобочки (как такая инициализация по-умному называется?).

Теперь введем специальный синтаксис для создания словарей и списков:

DictionaryAssert.AreMultiEquivalent(
{
ConstantParameterIds.BookFormat: {ConstantBookFormatValueIds.Paperback}
},
valuesDictionary);

Ну и заодно офтопом введем специальный синтаксис для кортежей:

var tuple = (5, 5); // syntax sugar for "new Tuple<int, int&пt;(5, 5)"

Закругляюсь, пока Хейлсберг не заметил :)

Как по-вашему, код стал лучше или хуже?

Update
В комментариях Shaddix предложил интересный вариант:

DictionaryAssert.AreEqual(
new []
{
Tuple.Create(ConstantParameterIds.BookFormat, new[] {ConstantBookFormatValueIds.Paperback})}
},
valuesDictionary.ToMultiArrayOfTuples());
, где ToMultiArrayOfTuples - метод расширения:

public Tuple<T, K>[] ToMultiArrayOfTuples(this IDictionary<T, List<K>> dictionary)
{
return dictionary.Select(x => Tuple.Create(x.Key, x.Value.ToArray())).ToArray();
}
Лично мне на этот тест приятнее смотреть: ничто не не отвлекает взгляд от того, какие данные мы ожидаем. Причем, в отличие от моих вышеизложенных предложений, этот код не является фантазией, а решает задачу минимизации синтаксической избыточности в рамках существующих возможностей C#.

12 комментариев:

Chesheersky комментирует...

всё круто, только вставки кода крайне неудобно читать :) может стоит разбить некоторые строки кода на несколько? :)
ПС пардон за оффтоп

Idsa комментирует...

Отформатировал код - теперь должно удобнее читаться

Chesheersky комментирует...

хм... с одной стороны, после того как Саня стал архитектором C# :), код стал "чище". Однако, после исчезновения всех "<>" стало не так очевидно с первого взгляда с чем имеешь дело. Як о двух концах ИМХО

Idsa комментирует...

Исчезновение <> меня мало беспокоит: ведь для методов вывод типов работает - и ничего. А вот представление словаря и списка в виде скобок выглядит довольно спорно. Здесь больше подошел бы синтаксис из JSON, но тогда непонятно, что делать с обратной совместимостью.

Idsa комментирует...

Я таки сделал последний пример чуть более JSON-подобным. Хотя в целом конкретный синтаксис значения не имеет - главное иметь возможность писать подобный код предельно лаконично.

Shaddix комментирует...
Этот комментарий был удален автором.
Shaddix комментирует...

Можно воспользоваться автоопределением типов при создании массивов. Получится что-то вроде:

CollectionAssert.AreEqual(
new []
{
Tuple.Create(ConstantParameterIds.BookFormat, new[] {ConstantBookFormatValueIds.Paperback})
}
asd.Select(x=>Tuple.Create(x.Key, x.Value.ToArray())).ToList());

Chesheersky комментирует...

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

Idsa комментирует...

Shaddix, красиво, но не очень прозрачно.

Shaddix комментирует...

Прозрачность - вопрос привычки :)
Да, когда видишь код первый раз и в одном месте - самый первый вариант - самый понятный.

Но если код в миллионе мест и очень похожий, то вот этот интерфейс инициализации Dictionary меня напрягал бы :)

А предложенный способ (он же фактически тобой был предложен изначально :)) очень удобен в случае, когда надо сравнить два списка объектов (а идентичность объектов определяется по паре property). Собственно, на подобных примерах и вырабатывается привычка, что CollectionAssert по Tuple'ам это хорошо.

Idsa комментирует...

Shaddix, а ты блин прав. Я вот сейчас прочитал твой последний комментарий, взглянул еще раз на код и проникся. Единственное, я бы последнюю строчку конвертации Dictionary вынес в extension method. И да, Tuple.Create смотрится громоздко - очень хочется специальный синтаксис, как в функциональщине.

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

Idsa комментирует...

Добавил Update с описанием предложенного Shaddix'ом варианта