Уровень вложенности скобочек


15 0

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

Вот я образно накидал фигню, которую я видел:

foreach (MyMegaObject o in objects)
{
  if (o != null) 
  {
    foreach (Object1 n in o.GetNames())
    {
      if (s != null)
      {
         if (s != " Something ")
         {
           Действия
         }
      }
    }
  }
}

На мой взгляд это ужас как страшно. Главная проблема в том, что автор думает оператором "не равно". Когда пишете код, не нужно думать отрицаниями, думайте утверждениями и постоянно утверждайте. Например, если переменную "o" написать утверждением, то можно исправить код следующим образом:

  if (o == null) 
    continue;

Это уже избавление от лишних скобок. Вторая проблема - автор боиться объединять несколько проверок. Возможно он думает, что если первая не сработает, то вторая будет выполняться в любом случае. Да, есть такие языки, которые всегда проверяют все, но компилятор C# и .NET достаточно интеллектуальны, чтобы отменять проверку, если первая накрылась медным тазом. Например, вы можете без проблем написать следующий код:

  if ((s == null) || (s.Value == " Something "))
    continue;

Автор исходника побоялся написать это скорей всего потому, что если переменная s будет равна нулю, то вторая проверка вызовит исключение. Это так, но только если она будет проверяться, но она проверяться НЕ БУДЕТ. .NET проверяет все последовательно и если одна из проверок накрывается, то все останавливается. Поэтому объединять можно, глвное писать в правильной последовательности. Например, следующий код сгенерирует ошибку:

  if ((s.Value == " Something ") || (s == null))
    continue;

В этом случае, если s == null, то будет сгенерировано исключение, потому что первым будет проверяться (s.Value == " Something ").

Итак, тот страшный скобочный код мог быть переписан так:

foreach (MyMegaObject o in objects)
{
  if (o == null) 
    continue;
  foreach (object1 n in o.GetNames())
  {
    if ((s == null) || (s.Value == " Something ")
      continue;
    Действия
  }
}

На мой глазастый взгляд это на много красивее.


Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым


Комментарии

ronin

10 Июня 2010

рефакторинг батюшка :) тоже планирую заняться такими вещами, а то столько ужасного кода накопилось за несколько лет


Николай

10 Июня 2010

Михаил, в книге, которую я сейчас читаю, от издательства APress, везде такой код. Как лучше оформлять? Я только начал учиться. :)


Михаил Фленов

10 Июня 2010

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


BasicWolf

10 Июня 2010

Михаил, а я наприме в С-подобных языках сделал для себя правилом отделать скобкой на отдельной строке namespace'ы, классы, методы, всё остальное (циклы, условия и т.д. - скобку ставить на той же строке. Код получается компактнее)


Михаил Фленов

10 Июня 2010

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


RainBoy

10 Июня 2010

Михаил, при всем уважении, но:
if ((s == null) || (s.Value == " Something ");
ИМХО, мусорный вариант. Как минимум, привязка к надежде на то, что в следующих версиях не поменяют логику работы, а в принципе - безолаберность программиста, который ввел потенциальный вылет ценой небольшого упрощения кода.


Михаил Фленов

10 Июня 2010

Это не меняется уже годами и скорей всего не поменяется, потому что все так пишут. Взгляни на заметку: http://flenov.info/blog.php?catid=746. Там явно ясходник MS и обрати внимание на эту конструкцию:


if (m_CacheBinding != null &&
                m_CacheBinding.Cache != null &&
                m_CacheBinding.Validator != null &&
                CacheProtocol == null &&
                policy != null &&
                policy.Level != RequestCacheLevel.BypassCache)
{
   CacheProtocol = new RequestCacheProtocol(m_CacheBinding.Cache, m_CacheBinding.Validator.CreateValidator());
}


Я уверен что ничего не измениться еще и потому, что это производительность. Нафига тратить время на все проверки, если уже первая валится. Разве что распараллеливать эту задачу по ядрам, но не думаю, что на это пойдут, оно того не стоит.


RainBoy

10 Июня 2010

Я не писал в общем случае про .NET
Просто "я уверен" - аргумент, конечно, хороший =)
Это больше сила привычки, Double checked так сказать =)


Михаил Фленов

10 Июня 2010

Просто для интереса, как ты думаешь, зачем они захотят сменить логику? Я знаю, зачем они не будут менять:
1. производительность
2. обратная совместимость (представляешь, сколько им самим придется кода переворошить после такой шутки)

Не вижу ни одной причины менять и не только в С++. Есть языки (точно не помню, но читал где-то, что есть какой-то один, может несколько языков), где по стандарту по любасу проверяются все части. Но я считаю это глупостью.


RainBoy

10 Июня 2010

Я не представляю варианта кроме явного бага в случае с .NET и данная конструкция будет работать.
Просто это стиль написания программ, и для меня он "грязный". Смысл делать потенциальное узкое место (пусть не .NET) мне непонятен. В запарке можно просто менять код и поменять местами операторы самому.


Михаил Фленов

10 Июня 2010

Ясно, но я не вижу смысла защищаться от багов .NET. Возможно, тут имеет даже смысл поднять спецификацию и посмотреть, как компилятор должен вести себя. Если он обязан проверять в такой последовательности, то не вижу ничего грязного. Хотя я не вижу в любом случае :)


Андрей

11 Июня 2010

RainBoy, менять никто ничиге не будет потому что есть && и &, || и |. И это не относится именно к .NET, это свойство языка C#. Такое поведение && и || сделано специально, чтобы ею пользовались. Подобное есть в C и C++.


Ustas

11 Июня 2010

if ((s == null) || (s.Value == " Something ");

Не придираюсь, ни в комем случае. Но не потеряна ли скопка в конце?

[Ради бога, не пропускай комментарий.]


Михаил Фленов

11 Июня 2010

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

if ((s.Value == " Something ") || (s == null));
  continue;

В реальности это будет выполнено как:

if ((s.Value == " Something ") || (s == null))
  ; // пустой оператор
continue;

То есть если условие верно, то выполниться пустой оператор (ничего не выполниться), а вот после этого в любом случае будет выполнен continue


Ustas

12 Июня 2010

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


Добавить Комментарий

Еще что-нибудь

Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.

О блоге

Программист, автор нескольких книг серии глазами хакера и просто блогер. Интересуюсь безопасностью, хотя хакером себя не считаю

Обратная связь

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

Пишите мне