C # становится все труднее читать?

15

По мере развития C # было добавлено много языковых возможностей. Это дошло до того, что это становится нечитаемым для меня.

В качестве примера рассмотрим следующий фрагмент кода из кода Caliburn.Micro здесь :

            container = CompositionHost.Initialize(
                   new AggregateCatalog(
                      AssemblySource.Instance.
                             Select(x => new AssemblyCatalog(x))
                               .OfType<ComposablePartCatalog>()
                )
            );

Теперь это только маленький пример.

У меня есть ряд вопросов:

  • Это распространенная или известная проблема?
  • Сообщество C # находит то же самое?
  • Это проблема с языком, или это стиль, используемый разработчиком?

Существуют ли простые решения для лучшего понимания кода других пользователей и предотвращения написания кода таким способом?

Стивен Эверс
источник
6
Это лямбда-функция, их трудно читать, пока вы их не получите.
Ryathal
9
Я думаю, что вы действительно не знали синтаксис так, как вы думали. С практикой чтения ваш пример прост.
ChaosPandion
6
@Thomas Owens: Святое дерьмо люди, по крайней мере , дать нам некоторое время для редактирования вопросов , чтобы держать их открытыми. 15 минут? Давай же.
Стивен Эверс
4
@DannyVarod: 1. «Нет» не является приемлемым ответом 2. Вопрос закрыт 3. Если вы согласны с тем, что вопрос не должен быть здесь, отметьте / проголосуйте, чтобы закрыть его или отредактировать, чтобы он был лучше .
Стивен Эверс
2
@ Thorbjørn Ravn Andersen - Чему можно научиться пощечину? Это не содержит никакой информации!

Ответы:

12

Краткое примечание о том, где находится язык, должно прояснить это: C # - язык программирования общего назначения ; в отличие от C (как C ++) он стремится к высокой абстракции; в отличие от диалектов Lisp он нацелен на практическую выразительность, и, в отличие от Java, он более агрессивен - Microsoft быстро реагирует на спрос.

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

Сообщество знает об этом, и, что более важно, ребята, стоящие за C #, прекрасно об этом знают (несколько записей в блогах и подкастов о том, что стояло за новыми дополнениями в C # 5.0, показывают, насколько плохо эти парни хотят упростить задачу). Microsoft будет пытаться взять часть нагрузки от своего флагманского таким образом, чтобы она не стала тарпиттинга: введение DLR, яркий прожектор над новыми языками (F #).

Кроме того, в материале курса по C # (включая сертификаты MS) рекомендуются различные (но согласованные) стили использования C # в зависимости от проблемы - выберите свое оружие и придерживайтесь его: в одних задачах LINQ в стиле Fluent, лямбды с TPL в других, простые Старый для большинства.

vski
источник
2
Можете ли вы объяснить, что вы имеете в виду, говоря «в отличие от диалектов Лисп он стремится к практической выразительности»?
Джорджио
27

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

Давайте возьмем два примера, оба на основе примеров, найденных в StackOverflow относительно одной и той же проблемы, и находим типы с определенным атрибутом:

C # 2.0:

static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{
    foreach(Assembly assembly in AppDomain.Current.GetAssemblies())
    {
        foreach(Type type in assembly.GetTypes()) 
        {
            if (type.IsDefined(typeof(TAttribute),inherit))
                yield return type;
        }
    }
}

C # 4.0:

IEnumerable<Type> GetTypesWith<TAttribute>(bool inherit) 
                              where TAttribute: System.Attribute
{ 
    return from assembly in AppDomain.CurrentDomain.GetAssemblies()
           from type in assembly.GetTypes()
           where type.IsDefined(typeof(TAttribute),inherit)
           select type;
}

ИМХО, новый синтаксис делает его намного проще для чтения.

Пит
источник
новый синтаксис действительно легче для чтения, но легче для понимания, как в том, что происходит под капотом? Первый пример понятен, второй более читабелен.
Nawfal
4
@nawfal По моему опыту, у людей гораздо больше проблем с пониманием конструкций «доходность», чем с операторами linq; так что я бы сказал, что второе более читаемо и понятно. Ни один из них на самом деле не говорит вам, что происходит под капотом, поскольку оба сопоставляют абстракции далеко от того, как на самом деле работают процессоры.
Chuu
Честно говоря, мне не нравится LINQ, потому что он абстрагирует циклы и рекомендует кодерам извлекать данные в отдельных запросах LINQ (с отдельными циклами в фоновом режиме) вместо одного цикла с более сложным ядром.
mg30rg
8

Когда был добавлен LINQ, он популяризировал стиль кодирования, включающий множество цепочек методов в стиле Fluent и передачу лямбда-выражений в качестве параметров.

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

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

var assemblyCatalog = AssemblySource.Instance
    .Select(x => new AssemblyCatalog(x))
    .OfType<ComposablePartCatalog>();
var aggregateCatalog = new AggregateCatalog(assemblyCatalog);
container = CompositionHost.Initialize(aggregateCatalog);
Carson63000
источник
1
Разве не странно создавать новый тип, а затем динамически включать этот тип сразу?
DeadMG
Возможно, я просто порезал фрагмент кода без какого-либо реального рассмотрения его намерений. Просто хотел разделить все экземпляры на их собственные строки кода, чтобы показать, как это повлияет на внешний вид кода.
Carson63000
Я беру на себя ответственность за отступы :(). Вот оригинал: devlicio.us/blogs/rob_eisenberg/archive/2010/07/08/...
1
Я полностью согласен с этим ответом. Проблема в примере кода не в новом синтаксисе C #, а в том, что писатель пытался быть умным с кодом «одной строки». Существует старое правило, появившееся в UNIX: все должно делать только одно , и делать это хорошо. Это относится к классу, это относится к методу и, конечно, это, безусловно, относится к строкам кода.
Лоран Бурго-Рой
4

Код в вашем примере не легко читается, потому что

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

  • Существует странное использование синглтона - AssemblySource.Instance.Select()подразумевается, что Instance является IEnumerable, что немного неудобно.

  • Переменная x в лямбда-выражении некрасива. Как правило, вы пытаетесь дать своим лямбда-переменным намерение раскрыть имена - помогает читателю определить, о каких типах данных предназначена функция, даже если он не знаком с лямбдами.

  • Не ясно, почему вы должны фильтровать с OfType<T>()коллекцией объектов, которые вы только что обновили ...

Однако все эти недостатки связаны с тем, как первоначальный разработчик написал код, и насколько выразительным он был для него. Это не что-то конкретное для последних версий C # и появления лямбда-выражений.

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

guillaume31
источник
2

Каждый раз, когда новая версия популярного языка получает новые конструкции, возникают подобные споры.

У меня также были эти сомнения много лет назад (http://gsscoder.blogspot.it/2009/08/use-csharps-features-wisely.html).

На мой взгляд, C # развивается элегантно и последовательно.

Код в вашем примере не сложен, возможно, вы не используете лямда-выражения.

Проблема в том, что C # - это не только объектно-ориентированный язык, он теперь поддерживает также функциональные конструкции (http://archive.msdn.microsoft.com/FunctionalCSharp/).

gsscoder
источник
1

Мне потребовалось немного времени, чтобы понять синтаксис лямбды, и я из области математики и физики. Это немного похоже на математические выражения, но они используют -> вместо =>:

Пример математики f (x): = x-> x + 2 это читается как «Функция f of x определяется как x отображается на x + 2

пример c # del myDelegate = x => x +2; это читается как «myDelegate - функция, такая, что myDelegate (x) отображает x на x + 2

Несоответствие между математикой и программированием на самом деле не помогает, хотя я полагаю -> уже рассматривалось в C ++ как «свойство» (urrgghh!)

http://en.wikipedia.org/wiki/List_of_mathematical_symbols

Энди Р
источник
msgstr "хотя я полагаю, что -> уже было занято в C ++ как" свойство "(urrgghh!)" Как будто! Плохо в C ++ это означает «свойство динамически размещаемого ...». Если что-то не динамично, вы используете точку, как на любом другом языке.
mg30rg