Отражение имени параметра: злоупотребление выражениями лямбда С# или яркостью синтаксиса?

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

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

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

   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 здесь внезапно я нахожу код, который активно смотрит и принимает решения на основе имен, которые я выбираю для своих переменных!

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

Ответ 1

Это плохое взаимодействие. Например, рассмотрим этот пример С# - F #

С#:

public class Class1
{
    public static void Foo(Func<object, string> f)
    {
        Console.WriteLine(f.Method.GetParameters()[0].Name);
    }
}

F #:

Class1.Foo(fun yadda -> "hello")

Результат:

"arg" печатается (не "yadda" ).

В результате разработчики библиотек должны либо избегать подобных "злоупотреблений", либо, по крайней мере, обеспечивать "стандартную" перегрузку (например, которая берет имя строки в качестве дополнительного параметра), если они хотят иметь хороший интерфейс. Чистые языки.

Ответ 2

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

.Attributes(new { style = "width:100%", @class="foo", blip=123 });

Это шаблон, используемый в большинстве ASP.NET MVC (например), и другие использует (a caveat, обратите внимание также мысли Айенде, если имя является магическим значением, а не вызывающим, специфические)

Ответ 3

Просто хотел выразить свое мнение (я являюсь автором грид-компонента MvcContrib).

Это определенно жестокое обращение с языком - не сомневайтесь. Тем не менее, я бы не стал считать это интуитивно понятным - когда вы смотрите на вызов Attributes(style => "width:100%", @class => "foo")
Я думаю, что это довольно очевидно, что происходит (это, безусловно, не хуже, чем подход анонимного типа). С точки зрения intellisense, я согласен, что это довольно непрозрачно.

Для интересующихся, некоторая справочная информация о ее использовании в MvcContrib...

Я добавил это в сетку как личное предпочтение - мне не нравится использование анонимных типов в качестве словарей (с параметром, который принимает "объект", столь же непрозрачен, как и тот, который принимает параметры Func []) и словарь инициализатор коллекции довольно многословный (я также не являюсь поклонником многословных интерфейсов, например, для объединения нескольких вызовов в атрибут ( "стиль", "отображение: нет" ). Атрибут ( "класс", "foo" ) и т.д. )

Если у С# был менее подробный синтаксис для словарных литералов, то я бы не стал беспокоиться, включая этот синтаксис в компоненте сетки:)

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

Кроме того, кто-то упомянул "накладные расходы на размышление", и я просто хотел указать, что на этот подход не так много накладных расходов - не задействовано отражение во время выполнения или компиляция выражения (см. http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx).

Ответ 4

Я бы предпочел

Attributes.Add(string name, string value);

Это гораздо более явный и стандартный, и ничего не получается, используя lambdas.

Ответ 5

Добро пожаловать в Rails Land:)

В этом нет ничего плохого, если вы знаете, что происходит. (Это, когда такого рода вещи не задокументированы хорошо, что есть проблема).

Вся структура Rails построена на идее условной конфигурации. Именование вещей определенным образом дает вам ключ к соглашению, которое они используют, и вы получаете совершенно бесплатно. Следуя соглашению об именах, вы попадаете туда, куда вы едете быстрее. Все это работает блестяще.

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

Ответ 6

Это ужасно на нескольких уровнях. И нет, это не похоже на Руби. Это злоупотребление С# и .Net.

Было много предложений о том, как сделать это более прямолинейно: кортежи, анонимные типы, свободный интерфейс и т.д.

Что делает его настолько плохим, так это то, что его просто способ представить себе для себя:

  • Что произойдет, когда вам нужно позвонить из VB?

    .Attributes(Function(style) "width:100%")

  • Его полностью противодействующий интуитивный, intellisense мало поможет понять, как передать материал.

  • Его излишне неэффективно.

  • Никто не будет знать, как его поддерживать.

  • Каков тип аргумента, входящего в атрибуты, это Func<object,string>? Как раскрывается это намерение. Какова будет ваша документация по intellisense: "Пожалуйста, не обращайте внимания на все значения объекта"

Я думаю, что вы полностью оправданы, испытывая эти чувства отвращения.

Ответ 7

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

Ответ 8

Оба. Это злоупотребление синтаксисом синтаксиса лямбда-выражений И.

Ответ 9

Я почти никогда не сталкивался с этим видом использования. Я думаю, что это "неуместно":)

Это не обычный способ использования, он несовместим с общими соглашениями. Такой синтаксис имеет свои плюсы и минусы:

против

  • Код не интуитивно понятен (обычные соглашения разные)
  • Он имеет тенденцию быть хрупким (переименование параметра нарушит функциональность).
  • Это немного сложнее проверить (при запуске API потребуется использовать отражение в тестах).
  • Если выражение интенсивно используется, оно будет медленнее из-за необходимости анализировать параметр, а не только значение (стоимость отражения)

Pros

  • Это более читаемо после того, как разработчик настроен на этот синтаксис.

Нижняя строка - в публичном дизайне API я бы выбрал более явный способ.

Ответ 10

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

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

.Attribute("style", "width:100%;").Attribute("class", "test")

Хотя это немного больше, чтобы печатать, он понятен и интуитивно понятен.

Ответ 11

Что случилось со следующим:

html.Attributes["style"] = "width:100%";

Ответ 12

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

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

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

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

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

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;
}

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

Ответ 16

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

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

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

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

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

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

или даже:

.Attributes.Style.Width.Percent = 100;

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

Ответ 17

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

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

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

Ответ 18

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

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

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

Ответ 19

По-моему, это злоупотребление лямбдами.

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

Ответ 20

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

Ответ 21

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