Отражение имени параметра: злоупотребление лямбда-выражениями C # или синтаксический блеск? - PullRequest
422 голосов
/ 12 ноября 2009

Я смотрю на компонент * Grid MvcContrib , и меня восхищает, но в то же время отталкивает синтаксический прием, используемый в синтаксисе Grid :

.Attributes(style => "width:100%")

Синтаксис выше устанавливает атрибут стиля сгенерированного HTML равным width:100%. Теперь, если вы обратите внимание, «стиль» нигде не указан, выводится из имени параметра в выражении! Я должен был покопаться в этом и найти, где происходит «волшебство»:

Hash(params Func<object, TValue>[] hash)
{
    foreach (var func in hash)
    {
        Add(func.Method.GetParameters()[0].Name, func(null));
    }
}

Таким образом, код использует формальное время компиляции, имя параметров для создания словаря пар имя-значение атрибута. Полученная синтаксическая конструкция действительно очень выразительна, но в то же время очень опасна. Общее использование лямбда-выражений позволяет заменять имена , используемые без побочных эффектов. Я вижу пример в книге, где написано collection.ForEach(book => Fire.Burn(book)) Я знаю, что могу написать в своем коде collection.ForEach(log => Fire.Burn(log)) и , это означает то же самое . Но с синтаксисом MvcContrib Grid здесь я неожиданно обнаружил код, который активно просматривает и принимает решения на основе имен, выбранных для моих переменных!

Так это обычная практика для сообщества C # 3.5 / 4.0 и любителей лямбда-выражений? Или мне не стоит беспокоиться о мошеннике-мошеннике?

Ответы [ 21 ]

17 голосов
/ 08 января 2010

Все эти разговоры о "ужасности" - это кучка давних ребят из c #, слишком остро реагирующих (а я давний программист на C # и до сих пор очень большой поклонник языка). В этом синтаксисе нет ничего ужасного. Это просто попытка сделать синтаксис похожим на то, что вы пытаетесь выразить. Чем меньше «шума» в синтаксисе чего-либо, тем легче программисту это понять. Уменьшение шума в одной строке кода только немного помогает, но пусть это накапливается во все большем и большем количестве кода, и это оказывается существенным преимуществом.

Это попытка автора стремиться к тем же преимуществам, которые дает вам DSL - когда код просто «похож» на то, что вы пытаетесь сказать, вы достигли волшебного места. Вы можете обсудить, хорошо ли это для взаимодействия, или же оно более приятное, чем анонимные методы, чтобы оправдать некоторые затраты на «сложность». Достаточно справедливо ... поэтому в вашем проекте вы должны сделать правильный выбор, использовать ли этот тип синтаксиса. Но все же ... это умная попытка программиста сделать то, что в конце концов мы все пытаемся сделать (осознаем мы это или нет). И то, что мы все пытаемся сделать, это: «Скажите компьютеру, что мы хотим, чтобы он делал, на языке, максимально приближенном к тому, как мы думаем о том, что мы хотим, чтобы он делал».

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

РЕДАКТИРОВАТЬ: я сказал "ключ к созданию программного обеспечения более удобной в обслуживании и более точной", что является сумасшедшим наивным преувеличенным единорогом. Я изменил его на «ключ».

17 голосов
/ 07 декабря 2009

Что не так со следующим:

html.Attributes["style"] = "width:100%";
17 голосов
/ 03 декабря 2009

Могу ли я использовать это, чтобы написать фразу?

magic lambda (n): лямбда-функция, используемая исключительно для замены магической строки.

12 голосов
/ 12 ноября 2009

Это одно из преимуществ деревьев выражений - можно проверить сам код на предмет дополнительной информации. Таким образом, .Where(e => e.Name == "Jamie") может быть преобразовано в эквивалентное предложение SQL Where. Это умное использование деревьев выражений, хотя я надеюсь, что оно не пойдет дальше этого. Что-то более сложное, вероятно, будет сложнее, чем код, который он надеется заменить, поэтому я подозреваю, что это будет самоограничением.

7 голосов
/ 03 декабря 2009

Это интересный подход. Если вы ограничите правую часть выражения только константами, то вы можете реализовать, используя

Expression<Func<object, string>>

Что я думаю, это то, что вы действительно хотите вместо делегата (вы используете лямбду, чтобы получить имена обеих сторон) Смотрите наивную реализацию ниже:

public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) {
    Dictionary<string, string> values = new Dictionary<string,string>();
    foreach (var func in hash) {
        values[func.Parameters[0].Name] = ((ConstantExpression)func.Body).Value.ToString();
    }
    return values;
}

Это может даже решить проблему межъязыкового взаимодействия, о которой упоминалось ранее в теме.

6 голосов
/ 12 ноября 2009

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

Как вы указали, теперь существует неясная зависимость между именем параметра (стилем) и атрибутом HTML. Проверка времени компиляции не производится. Если имя параметра набрано неправильно, вероятно, на странице не будет сообщения об ошибке во время выполнения, но будет гораздо сложнее найти логическую ошибку (без ошибок, но некорректное поведение).

Лучшим решением было бы иметь элемент данных, который можно проверить во время компиляции. Так что вместо этого:

.Attributes(style => "width:100%");

код со свойством Style может быть проверен компилятором:

.Attributes.Style = "width:100%";

или даже:

.Attributes.Style.Width.Percent = 100;

Это большая работа для авторов кода, но этот подход использует преимущества сильной проверки типов в C #, которая в первую очередь помогает предотвратить попадание ошибок в код.

5 голосов
/ 02 декабря 2009

ИМХО, это крутой способ сделать это. Мы все любим тот факт, что присвоение класса Controller сделает его контроллером в MVC, верно? Так что есть случаи, когда наименование имеет значение.

Также намерение здесь очень ясно. Очень легко понять, что .Attribute( book => "something") приведет к book="something", а .Attribute( log => "something") приведет к log="something"

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

5 голосов
/ 12 ноября 2009

на самом деле это похоже на Ruby =), по крайней мере для меня использование статического ресурса для более позднего динамического «поиска» не подходит для целей проектирования API, надеюсь, что этот хитрый прием не является обязательным в этом API.

Мы могли бы наследовать от IDictionary (или нет) и предоставить индексатор, который ведет себя как массив php, когда вам не нужно добавлять ключ для установки значения. Это будет допустимое использование семантики .net, а не только c #, и все еще нуждается в документации.

надеюсь, это поможет

4 голосов
/ 12 ноября 2009

На мой взгляд, это злоупотребление лямбдами.

Что касается блеска синтаксиса, я нахожу style=>"width:100%" довольно запутанным. Особенно из-за => вместо =

3 голосов
/ 12 ноября 2009

Если имена методов (func) выбраны правильно, то это отличный способ избежать проблем с обслуживанием (т. Е. Добавить новый func, но забыл добавить его в список отображения функций-параметров). Конечно, вам нужно тщательно документировать это, и вам лучше будет автоматически сгенерировать документацию для параметров из документации для функций этого класса ...

...