Последнее время меня несколько раз спрашивали относительно стандартов и правил оформления кода на C# и T-SQL , поэтому я решил выложить их в открытый доступ.
Стандарты и правила доступны по следующим ссылкам на моем сайте:
Стандарты и правила доступны по следующим ссылкам на моем сайте:
- Стандарт оформления кода C#: http://www.msmirnov.ru/public/CSharp_Coding_Standards.doc
- Стандарт оформления кода T-SQL: http://www.msmirnov.ru/public/TSQL_Coding_Standards.doc
Вообще существуют кое-какие общепринятые стандарты.
ОтветитьУдалитьИ существует такой формальный документ от Майкрософт, который когда-то (я просто давно его не видел) вполне неплохо соответствовал этим общепринятым стандартам.
Твой вариант, например, противоречит. В частности в пункте, касающемся акронимов в идентификаторе.
Вариант MS (он же общепринятый, он же используется во фреймворке):
SqlCommand, XmlSerializer
В твоём варианте получается:
SQLCommand, XMLSerializer, что гораздо менее читабельно, не соответствует, непонятно, где заканчивается один акроним и начинается другой (буде такое встретится), да и многие тулзы типа Решарпера будут на это указывать и ныть - ну переименуй, ну переименуй по-человечески-то :)
Кстати, от подобного MS отказалась толи при переходе от FW1.0 к 1.1, толи при переходе от беты к релизу, не помню. Но они осознали свою ошибку :)
Поем - ещё почитаю.
@Michael Smirnov спасибо за публикацию своих доков. Любопытно посмотреть разные стандарты разных компаний :)
ОтветитьУдалитьНу, Леш, это не лично только мои стандарты - они были выработаны нами сообща, несколько лет назад, на основе собственного накопленного опыта и анализа нескольких других стандартов. Они прошли довольно много обсуждений и были завершены в том виде, который частично сложился у нас и частично были заимствованы из других источников.
ОтветитьУдалитьНо, замечания, конечно, высказывать можно :)
@Anton Vishnyakov всегда пожалуйста!
ОтветитьУдалитьЕсли у вас что-то есть интересное - тоже было бы интересно посмотреть.
@Michael Smirnov интересного ничего нет. В Терралинке был такой, его давали читать в первый день. Но он там скорее как дань корпоративному стилю и на практике все было как обычно.
ОтветитьУдалитьЯ бы поставил решарпер всем и не извращался стопками бумаг - это если контексте c#. Кстати, почему это не работает? - решарпера недостаточно для этого или что? Зачем писать манифесты подобного рода?
Удивился, так как первый раз увидел в стандарте префиксы asp.net. Вот это - реальны гемор и действительно полезная фича. Это классно - т.е. видно, что люди напарились на asp.net и внесли решение в стандарт, а не сделали стандарт для галочки. Ну т.е. для меня этот момент показывает то, что стандарт вроде как "живой" и рожден в ходе каких то дискуссий и опыта :)
А можно ли эти стандарты не писать в виде бесконечных-нечитаемых-никем документов, а зафигачить в решарпер? :)
Антон, большое спасибо за лестный отзыв!
ОтветитьУдалитьКстати, данные документы были составлены еще при работе на Visual Studio 2005.
По поводу нечитаемых документов - дело в том, что в момент их создания мы пришли к общему осознанию того, что нам необходимо утвердить стандарты кодирования.
Мы проанализировали накопленный опыт и ряд внешних источников и результат работы изложили в данных документах.
Поэтому, данные стандарты получились как бы естественным развитием того, что у нас было в тот момент, а не насаждались сверху и с нуля. Так что в плане их внедрения все было не особенно сложно.
Ну я просто высказываю свою точку зрения, не претендую на истину, но пытаюсь аргументировать :)
ОтветитьУдалитьНапример, про акронимы в таком виде - получается зоопарк, на мой взгляд. Все либы и фреймворк следуют одному стандарту, а твой код - другому. Сие мне не нравится :)
Ну и ещё немного придирок и чисто моего, ни к чему (кроме "подумать") не обязывающего мнения :)
При создании классов потомков их имена состоят из имени базового класса и суффикса класса потомка, если суффиксов несколько – они разделяются символом подчеркивания.
Вот лично я против ничего не значащих имён классов типа 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, тока попробуй что-то поменять - сразу получишь исключение. Никаких правил для этого не нужно, так как контроля над этим поведением всё равно никакого нет.
Про исключения - согласен, но удив
лён, что не увидел явного запрета на использование catch(Exception) и на использование пустого catch {} (поймали исключение и пошли дальше, как будто ничего и не было) :)
ОтветитьУдалитьПро обработку ошибок можно ещё много чего сказать... :)
P.S. А в некоторых языках вообще нет операторов цикла ;) Потому что нафига, если есть рекурсия ;) С другой стороны, к рекурсии в C# действительно следует относиться осторожно, так как компилятор, к сожалению, не умеет генерировать код "хвостовой" рекурсии :(
Да, Леш, согласен, наш стандарт ведь тоже не истина в последней инстанции.
ОтветитьУдалитьНа счет составных имен классов наследников - в общем случае я с тобой в чем-то согласен, но просто в нашей ситуации так сделать было удобнее.
@Alexey Raga
ОтветитьУдалить>MyFunc, где понятно, что T4 - возвращаемое значение
Не понимаю в упор, почему T4, а не TResult. Во-первых, когда идет нагромождение генериков, не всегда увидишь в интеллисенсе, что там за возвращаемое значение у функции. А в женерик-параметрах оно может и не передаваться, а определятся к примеру из контекста экстеншн-метода, или задаваться жестко, или определяться на основе T4, но не являться им (простой пример - IEnumerable).
В общем, на мой взгляд, случаи когда приведенный пример может быть плохой практикой (т.е. он запутает человека, который считает что T4 это всегда тип возвращаемого параметра), существуют.
Поэтому, лично я всегда пишу TResult, и кстати, в самом фреймворке частенько такую же практику видел. В linq'е.
да, и еще хотел удивиться по поводу вот этой конструкции
ОтветитьУдалить>public User { get { return _user; } }
а что, случаи когда есть необходимость использовать подобные свойства действительно бывают? :)
ну, я про то, что вроде бы вариант
public User { get; private set; }
значительно класснее выглядит, и случаев, когда его нельзя использовать, я вроде и не встречал. ну не знаю, разве что когда нужно пометить поле каким-то атрибутом, который на свойство нельзя повесить.. и то это обходится
@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
бррр :)
Но на самом деле всё это достаточно мало значения имеет. Самое большое значение имеет тот пункт, который я описал как вредный.
Я тоже за ловлю нетипизированных исключений - всегда бью по рукам. И на собеседовании всегда спрашиваю, чего плохого в ловле нетипизированных исключений, и когда их ловить всё же можно.
ОтветитьУдалитьДокументы не читал, займусь чуть позже.
@Alexey Raga
ОтветитьУдалить>Просто потому, что последнее значение всегда возвращаемое.
ну вот не знаю, на мой взгляд лишнее напоминание о значении последнего аргумента не повредит. хотя бы из того, что даже если человек вообще не знает что такое функтор, он все равно не запутается:) да и вообще, хорошее это правило, когда все параметры со сколь нибудь специфичным значением должны соответственно описываться... в том числе - параметры-типы для женериков.
>public User { get { return Bag.Get(this); } }
ну, я имел в виду в большей степени избавление от паразитного объявления приватного поля, которое помимо лишней писанины, делает код менее читаемым и вносит путаницу в имена. в общем, хотелось бы чтобы свойство было некой неделимой сущностью, а не искать, какое-же там паразитное поле к этому свойству относится, и есть ли вообще оно:)
>foreach (x in xs) x.FixFrickingUpYourself();
ужс:)
не, ну в целом согласен, что не стоит раздувать какие-то банальные вещи в мегамоты.
все от контекста зависит.
однако, практику форича в одну строку впервые вижу:))
выглядит страшненько... если честно я б предпочел сделать экстеншн метод какой-нить, если уж настолько часто требуется.
т.е. итоговый вид был бы
xs.ForEach(x => x.FixFrickingUpYourself());
>a == b &&
>b != c
вот это ты в точку! :)
это согласен, давно пора вводить во все стандарты, в которых еще нет!:)
>Но на самом деле всё это достаточно мало значения имеет. Самое большое значение имеет тот пункт, который я описал как вредный.
тоже согласен) ну я написал про пункты которые были не до конца понятны :)
xs.ForEach(x => x.FixFrickingUpYourself());
ОтветитьУдалитьЛогично, но я решил этого не делать, так как мелкая конструкция усложняются лямбдой, решил что оно того не стоит... К тому же я предпочитаю, чтобы функция что-то возвращала... Оттого и у листа ForEach не люблю.
Кржиштоф Цвалина и Брэд Абрамс в своей книге "Инфраструктура программных проектов. Соглашения, идиомы и шаблоны для многократно используемых библиотек .NET" предлагают стандарты кодирования на c#. Причем у них нет однозначности - делай только так и не иначе, а рассматриваются возможные варианты исходя из различных соображений. Команда же определятся сама, что ей больше подходит.
ОтветитьУдалить