четверг, 13 января 2011 г. - www.msmirnov.ru

Стандарты и правила оформления кода C# и T-SQL

Последнее время меня несколько раз спрашивали относительно стандартов и правил оформления кода на C# и T-SQL , поэтому я решил выложить их в открытый доступ.

Стандарты и правила доступны по следующим ссылкам на моем сайте:
Мой сайт - www.msmirnov.ru

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

  1. Вообще существуют кое-какие общепринятые стандарты.
    И существует такой формальный документ от Майкрософт, который когда-то (я просто давно его не видел) вполне неплохо соответствовал этим общепринятым стандартам.

    Твой вариант, например, противоречит. В частности в пункте, касающемся акронимов в идентификаторе.
    Вариант MS (он же общепринятый, он же используется во фреймворке):
    SqlCommand, XmlSerializer
    В твоём варианте получается:
    SQLCommand, XMLSerializer, что гораздо менее читабельно, не соответствует, непонятно, где заканчивается один акроним и начинается другой (буде такое встретится), да и многие тулзы типа Решарпера будут на это указывать и ныть - ну переименуй, ну переименуй по-человечески-то :)

    Кстати, от подобного MS отказалась толи при переходе от FW1.0 к 1.1, толи при переходе от беты к релизу, не помню. Но они осознали свою ошибку :)

    Поем - ещё почитаю.

    ОтветитьУдалить
  2. @Michael Smirnov спасибо за публикацию своих доков. Любопытно посмотреть разные стандарты разных компаний :)

    ОтветитьУдалить
  3. Ну, Леш, это не лично только мои стандарты - они были выработаны нами сообща, несколько лет назад, на основе собственного накопленного опыта и анализа нескольких других стандартов. Они прошли довольно много обсуждений и были завершены в том виде, который частично сложился у нас и частично были заимствованы из других источников.

    Но, замечания, конечно, высказывать можно :)

    ОтветитьУдалить
  4. @Anton Vishnyakov всегда пожалуйста!
    Если у вас что-то есть интересное - тоже было бы интересно посмотреть.

    ОтветитьУдалить
  5. @Michael Smirnov интересного ничего нет. В Терралинке был такой, его давали читать в первый день. Но он там скорее как дань корпоративному стилю и на практике все было как обычно.

    Я бы поставил решарпер всем и не извращался стопками бумаг - это если контексте c#. Кстати, почему это не работает? - решарпера недостаточно для этого или что? Зачем писать манифесты подобного рода?

    Удивился, так как первый раз увидел в стандарте префиксы asp.net. Вот это - реальны гемор и действительно полезная фича. Это классно - т.е. видно, что люди напарились на asp.net и внесли решение в стандарт, а не сделали стандарт для галочки. Ну т.е. для меня этот момент показывает то, что стандарт вроде как "живой" и рожден в ходе каких то дискуссий и опыта :)

    А можно ли эти стандарты не писать в виде бесконечных-нечитаемых-никем документов, а зафигачить в решарпер? :)

    ОтветитьУдалить
  6. Антон, большое спасибо за лестный отзыв!

    Кстати, данные документы были составлены еще при работе на Visual Studio 2005.

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

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

    ОтветитьУдалить
  7. Ну я просто высказываю свою точку зрения, не претендую на истину, но пытаюсь аргументировать :)
    Например, про акронимы в таком виде - получается зоопарк, на мой взгляд. Все либы и фреймворк следуют одному стандарту, а твой код - другому. Сие мне не нравится :)

    Ну и ещё немного придирок и чисто моего, ни к чему (кроме "подумать") не обязывающего мнения :)

    При создании классов потомков их имена состоят из имени базового класса и суффикса класса потомка, если суффиксов несколько – они разделяются символом подчеркивания.
    Вот лично я против ничего не значащих имён классов типа DBObjectPersonUserAdministrator (или, ещё хуже, с подчёркиваниями). В какой-то мере это даже нарушает принцип сокрытия, раскрывая детали реализации. Я за то, чтобы имя типа описывало себя и своё назначение, а не свою ирерархию. Ну и подчёркивания в именах типов я тоже не одобряю.

    Про Generics и T - сам раньше так думал. Но (быть может под влиянием функциональных языков) тоже перешёл на "стандартную" сторону: T, V, U и т.д.
    А в случае, когда параметры друг от друга отличать не надо, то просто T1, T2, T3...
    Пример: MyFunc, где понятно, что T4 - возвращаемое значение, а остальные параметры ничем кроме порядка друг от друга не отличаются.

    Но самая большая проблема на мой взгляд - это концепция "больших", "основных" и "вспомогательных" функций. Даже не потому, что там используются disgusting подчёркивания, а потому, что это взгляд с неправильной стороны.
    Правильная сторона тут очевидна и всего одна: функция делает только одну вещь и в своём названии явно указывает, что она делает. Вот и всё. Такая функция "большой" быть не может. А если может - то, значит, это так правильно (кто знает, может там switch-case на 150 элементов).
    Функция по определению, делая свой кусочек задачи, не знает и не должна знать о том, кто, как и когда будет её использовать, вызывается ли она только из одной функции или из десяти, это не её зона ответственности. Функция не может быть "вспомогательной". Всё, что она может - это определить зону своей видимости. Всё. Реально. То, что одни функции вызывают другие, кубички побольше строятся из кубичков поменьше - это нормально и это называется композиция. Кубички поменьше не являются "вспомогательными", они просто есть. Сейчас функцию используют один раз, завтра - два, послезавтра вынесли в библиотеку. Не переименовывать же её всегда, это самой функции и её имени не касается.
    Всё остальное - это ошибки дизайна.
    Считаю этот пункт ВРЕДНЫМ.

    Мы ещё используем префикс подчёркивание для приватных полей класса.
    Это противоречит документу MS (в том виде, как я его видел давно), но соответствует общепринятому стандарту.
    На практике это позволяет избежать ситуации "совпадения с параметрами и использования this" и исключить этот пункт из свода правил.
    Как побочный эффект - при сортировке в подсказках "студии" поля идут одним скопом (бо начинаются с одного символа), что позволяет их легко пропускать, либо находить :)
    Но вообще, при использовании auto properties количество приватных полей сводится к почти нулю...

    Лично я (тоже влияние FP) короткие свойства описываю полностью на одной строке:
    public User { get { return _user; } }

    Про комментарии - отлично. Никак не могу пробить на работе :(

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

    Про foreach и модификацию коллекций - тоже не понял. За этим следит runtime, тока попробуй что-то поменять - сразу получишь исключение. Никаких правил для этого не нужно, так как контроля над этим поведением всё равно никакого нет.

    Про исключения - согласен, но удив

    ОтветитьУдалить
  8. лён, что не увидел явного запрета на использование catch(Exception) и на использование пустого catch {} (поймали исключение и пошли дальше, как будто ничего и не было) :)
    Про обработку ошибок можно ещё много чего сказать... :)

    P.S. А в некоторых языках вообще нет операторов цикла ;) Потому что нафига, если есть рекурсия ;) С другой стороны, к рекурсии в C# действительно следует относиться осторожно, так как компилятор, к сожалению, не умеет генерировать код "хвостовой" рекурсии :(

    ОтветитьУдалить
  9. Да, Леш, согласен, наш стандарт ведь тоже не истина в последней инстанции.

    На счет составных имен классов наследников - в общем случае я с тобой в чем-то согласен, но просто в нашей ситуации так сделать было удобнее.

    ОтветитьУдалить
  10. @Alexey Raga
    >MyFunc, где понятно, что T4 - возвращаемое значение
    Не понимаю в упор, почему T4, а не TResult. Во-первых, когда идет нагромождение генериков, не всегда увидишь в интеллисенсе, что там за возвращаемое значение у функции. А в женерик-параметрах оно может и не передаваться, а определятся к примеру из контекста экстеншн-метода, или задаваться жестко, или определяться на основе T4, но не являться им (простой пример - IEnumerable).

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

    Поэтому, лично я всегда пишу TResult, и кстати, в самом фреймворке частенько такую же практику видел. В linq'е.

    ОтветитьУдалить
  11. да, и еще хотел удивиться по поводу вот этой конструкции
    >public User { get { return _user; } }
    а что, случаи когда есть необходимость использовать подобные свойства действительно бывают? :)

    ну, я про то, что вроде бы вариант
    public User { get; private set; }
    значительно класснее выглядит, и случаев, когда его нельзя использовать, я вроде и не встречал. ну не знаю, разве что когда нужно пометить поле каким-то атрибутом, который на свойство нельзя повесить.. и то это обходится

    ОтветитьУдалить
  12. @Andrey Markeev
    MyFunc, где понятно, что T4 - возвращаемое значение
    Просто потому, что последнее значение всегда возвращаемое. Для вещей типа Func имя значения не имеет, а для тех случаев, когда имеет (TKey, TValue и т.д.) вопрос не стоит :)

    public User { get { return _user; } }
    Ага, бывают. С атрибутом правильно угадано - NonSerialized атритбут вешать иногда хочется. Обходить? Ради чего? Ради того, чтобы не писать на одной строчке? Это смешно.
    Кроме того, часто бывают случаи типа:
    public User { get { return Model.User; } }
    или
    public User { get { return Bag.Get(this); } }=
    да мало ли. Take it as an example.

    Я и функции, бывает, на одной строчке пишу (тоже просто пример из головы):
    public long CountActiveUsers() { return Users.Where(x=>x.IsActive); }

    Так же я не ставлю фигурные скобки в однострочных if-else:

    if (isGod)
    CreateUniverse()
    else
    GoAndEatSomeApples();

    употребляю ?: достаточно часто:
    return activeOnly
    ? GetActiveUsers()
    : Users.AsEnumerable();

    часто пишу foreach на одной строке:
    foreach (x in xs) x.FixFrickingUpYourself();

    И ещё я очень не люблю, когда люди ставят точку на одной строчке, а имя метода пишут на другой :) Аналогичная ситуация с условиями, когда пишут:
    a == b &&
    b != c
    бррр :)

    Но на самом деле всё это достаточно мало значения имеет. Самое большое значение имеет тот пункт, который я описал как вредный.

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

    Документы не читал, займусь чуть позже.

    ОтветитьУдалить
  14. @Alexey Raga
    >Просто потому, что последнее значение всегда возвращаемое.
    ну вот не знаю, на мой взгляд лишнее напоминание о значении последнего аргумента не повредит. хотя бы из того, что даже если человек вообще не знает что такое функтор, он все равно не запутается:) да и вообще, хорошее это правило, когда все параметры со сколь нибудь специфичным значением должны соответственно описываться... в том числе - параметры-типы для женериков.

    >public User { get { return Bag.Get(this); } }
    ну, я имел в виду в большей степени избавление от паразитного объявления приватного поля, которое помимо лишней писанины, делает код менее читаемым и вносит путаницу в имена. в общем, хотелось бы чтобы свойство было некой неделимой сущностью, а не искать, какое-же там паразитное поле к этому свойству относится, и есть ли вообще оно:)

    >foreach (x in xs) x.FixFrickingUpYourself();
    ужс:)
    не, ну в целом согласен, что не стоит раздувать какие-то банальные вещи в мегамоты.
    все от контекста зависит.
    однако, практику форича в одну строку впервые вижу:))
    выглядит страшненько... если честно я б предпочел сделать экстеншн метод какой-нить, если уж настолько часто требуется.
    т.е. итоговый вид был бы
    xs.ForEach(x => x.FixFrickingUpYourself());

    >a == b &&
    >b != c
    вот это ты в точку! :)
    это согласен, давно пора вводить во все стандарты, в которых еще нет!:)

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

    ОтветитьУдалить
  15. xs.ForEach(x => x.FixFrickingUpYourself());
    Логично, но я решил этого не делать, так как мелкая конструкция усложняются лямбдой, решил что оно того не стоит... К тому же я предпочитаю, чтобы функция что-то возвращала... Оттого и у листа ForEach не люблю.

    ОтветитьУдалить
  16. Кржиштоф Цвалина и Брэд Абрамс в своей книге "Инфраструктура программных проектов. Соглашения, идиомы и шаблоны для многократно используемых библиотек .NET" предлагают стандарты кодирования на c#. Причем у них нет однозначности - делай только так и не иначе, а рассматриваются возможные варианты исходя из различных соображений. Команда же определятся сама, что ей больше подходит.

    ОтветитьУдалить