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

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

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

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

среда, 12 января 2011 г. - www.msmirnov.ru

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

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

Для загрузки Стандарты и правила доступны по следующей ссылке на моем сайте: http://www.msmirnov.ru/public/TSQL_Coding_Standards.doc

Либо с ними можно ознакомиться прямо здесь.

       



Стандарт оформления кода C#




Цель документа


       Документ является соглашением по оформлению и написанию кода на языке C#. В документе приведены основные правила оформления кода и приемы, используемые при написании программ.

Цели документа:


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

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


Стили именования



  • Pascal case первая буква каждого слова в имени идентификатора начинается с верхнего регистра.

Пример: TheCategory;


  • Camel case первая буква первого слова в идентификаторе в нижнем регистре, все первые буквы последующих слов в верхнем.

Пример: theCategory;


  • UpperCase стиль используется только для сокращений, все буквы в имени идентификатора в верхнем регистре.

Пример: ID;



  • Hungarian notation перед именем идентификатора пишется его тип в сокращенной форме.

Пример: strFirstName, iCurrentYear.


Правила именования идентификаторов


Общие правила именования идентификаторов




  • При именовании идентификаторов не используются аббревиатуры или сокращения, если только они не являются общепринятыми.

Пример: GetWindow(), а не GetWin();


  • Если имя идентификатора включает в себя сокращение сокращение пишется в upper case. Исключение - когда имя идентификатора должно быть указано в camel case и сокращение стоит в начале имени идентификатора. В этом случае сокращение пишется в нижнем регистре.

Пример:
PPC
Account (PPC сокращение от pay per click) для pascal case,
ppc
Account для camel case.



Использование верхнего и нижнего регистра в именах




  • Запрещается создавать два различных namespaceа, функции, типа или свойства с одинаковыми именами, отличающиеся только регистром. Запрещается создавать функции с именами параметров, отличающимися только регистром. Ниже приведены примеры НЕправильных названий.

Пример:       
               Keyword
Manager и Keywordmanager;
               KeywordManager.
Keyword и KeywordManager.KEYWORD;
               int
id {get, set} и int ID {get, set};

f
indByID(int id) и FindByID(int id);
void MyFunction(string
s, string S).



Правила именования классов



  • Следует избегать имен классов, совпадающих с именами классов .NET Framework;

  • Для классов используется стиль именования pascal case;

  • Для классов, унаследованных от CollectionBase используется суффикс Collection, перед которым указывается тип объектов, для которых используется коллекция.

Пример: UserCollection, CompanyCollection;


  • В качестве имен классов используются имена существительные;

  • Имя класса не должно совпадать с именем namespaceа.


Пример: namespace Debugging, класс Debug;


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


Пример: таблица Users, класс User;


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

Пример:
базовый класс Figure,
потомок Figure
Circle;


  • Имена файлов, в которых находятся классы, совпадают с именами классов. Для именования файлов используется стиль pascal case.



Правила именования интерфейсов

       

Имена интерфейсов начинаются с буквы I, после которой следует название интерфейса в pascal case.

Пример: IDisposable.





Правила именования genericов




Genericи обозначаются буквой T, если genericов несколько их имена начинаются с буквы T.

Пример: GetItems<T>(int parentID)



Правила именования функций



  • Для именования функций используется стиль pascal case;

  • Функции объявляются согласно следующему шаблону:

<Модификатор доступа> [Другие модификаторы] <Тип> <Название функции>();


Пример: protected abstract void HelloWorld();

  • Имена функций должны давать четкое представление о том, какое действие эта функция выполняет. Имя функции начинается с глагола, указывающего на то, какое действие она выполняет;

  • Большие функции, не умещающиеся на одном экране, делятся на несколько private функций меньшего размера, имена таких вспомогательных функций состоят из имени основной (большой) функции и существительного, глагола или фразы, которые уточняют действие вспомогательной функций, разделенные подчеркиванием. Основная и вспомогательная функции объединяются в регионы. Вспомогательные функции вызываются только из основной функции.


Пример:
основная функция
                       CheckProduct,
вспомогательные функции
       CheckProduct_Price,
                                                         CheckProduct_
Url,
                                                         CheckProduct
_SearchTerm.



Правила именования параметров функций


  • Для именования параметров используется стиль camel case;


  • Имена параметров должны давать четкое представление о том для чего используется параметр, и какое значение следует передать при вызове функции.

Пример:
public void EncodeString(string
sourceString, ref string encodedString),
а
не public void EncodeString(string string1, ref string string2).



  • В том случае, когда это не препятствует понимаю кода, в качестве имени параметра функции используется имя соответствующего параметру класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.

Пример:
UserFactory.Create(Company
company);
      
CheckUsers(UserCollection users);



  • Имена параметров не должны совпадать с именами членов класса, если этого не удается избежать, то для разрешения конфликтов используется ключевое слово this.

Пример:
       
public void CreateUser(string firstName, string lastName)
{
      
this.firstName  = firstName;
      
this.lastName = lastName;
}

  • В именах параметров не используется венгерская нотация.




Правила именования свойств



  • Для именования свойств используется стиль pascal case;

  • Свойства объявляются согласно следующему шаблону:

<Модификатор доступа> [Другие модификаторы] <Тип> <Название свойства>;

Пример: public static User CurrentUser        { get; }



  • В том случае, когда это не препятствует понимаю кода, в качестве имени свойства используется имя соответствующего свойству класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.

Пример:
public User User { get; set; }
public UserCollection User
s  { get; set; }


  • Название свойства типа bool должно представлять из себя вопрос, требующий ответа да или нет.

Примеры названий: “CanDownload”, “HasKeywords”, “IsChecked”, “NeedsUpdate”.



Правила именования полей



  • Для именования полей, доступных вне класса, используется стиль pascal case, для private полей - camel case;


  • Поля объявляются согласно следующему шаблону:

<Модификатор доступа> [Другие модификаторы] <Тип> <Название поля>;

Пример: private static User currentUser = null;



  • В том случае, когда это не препятствует понимаю кода, в качестве имени поля используется имя соответствующего полю класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.

Пример:
public User User = new User();
public UserCollection User
s = new UserCollection();

  • Название поля типа bool должно представлять из себя вопрос, требующий ответа да или нет.

Примеры названий: “CanDownload”, “HasKeywords”, “IsChecked”, “NeedsUpdate”.






Правила именования переменных



  • Для именования переменных используется стиль camel case;

  • Переменные объявляются согласно следующему шаблону:
<Тип> <Название поля>;


Пример: int userID = null;


  • В циклах foreach имя переменной назначается как имя массива в единственном числе.

Пример: foreach(Campaign newCampaign in NewCampaigns).



Правила именования констант


  • Для именования констант используется стиль pascal case.


Правила именования enumов




  • Для именования enumов и их значений используется стиль pascal case;
  • Имена enumов указываются в единственном числе
  • Имена, как правило, состоят из имени сущности, к которой относится enum и названия содержимого enumа (status, type, state).

Пример: KeywordStatus, ConnectionState, TaskType.



Правила именования exceptionов



  • Для exceptionов используется стиль pascal case;
  • Имена классов для создаваемых custom exceptionов заканчиваются суффиком Exception;
  • В качестве имени объекта исключения внтури catch, для исключений типа Exception, используется имя “ex”.

Пример: catch(Exception ex).


Правила именования controlов в asp.net



  • Для именования controlов используется венгерская нотация (стиль Hungarian notation). См. Приложение 1: Префиксы, используемые для именования контролов в asp.net.


Форматирование кода



  • Используются стандартные настройки форматирования Visual Studio;

  • В одном файле не объявляется больше одного namespaceа и одного класса (исключение небольшие вспомогательные private классы);

  • Фигурные скобки размещаются всегда на отдельной строке;

  • В условии if-else всегда используются фигурные скобки;

  • Размер tabа 4;

  • Использование строк длиннее 100 символов не желательно. При необходимости инструкция переносится на другую строку. При переносе части кода на другую строку вторая и последующая строки сдвигаются вправо на один символ табуляции;

  • Каждая переменная объявляется на отдельной строке;

  • Все подключения namespaceов (using) размещаются в начале файла, системные namespaceы объявляются над custom namespaceами;

  • Если для свойства существует соответствующее поле (например, при загрузке по требованию), то поле объявляется над свойством.
Пример:
               private User
user;
               public User
User { get; set; }

  • Если set или get свойства состоит из одной операции весь set или get размещается на одной строке.
Пример:
      
Public User
      
{       
                    
get        {        return user;        }       
       }

  • Функции, поля и свойства группируются внутри класса по своему назначению. Такие группы объединяются в регионы;






Комментирование кода



  • Все комментарии должны быть на русском языке;

  • Для функций, классов, enumов, свойств и полей комментарии необходимо указывать в таком виде, чтобы по ним можно было автоматически сгенерировать документацию. Для этого используются стандартные tagи такие как <summary>, <param> и <return>.

  • Для функций создающих exceptionы возможные исключения необходимо указывать в tagах <exception>;

  • Для включения в документацию примеров использования необходимо применять tagи <example>, <remarks> и <code>

  • Для ссылок в документации необходимо использовать tagи <see cref=””/> и <seeAlso cref=””/>.

  • При использовании в тексте комментариев символов, использующихся в xml как спецсимволы,  необходимо использовать tag CDATA.

  • Комментарии к заголовкам функций, свойств, полей, интерфейсов и прочего необходимо указывать всегда;

  • Для функций, выполняющих сложные алгоритмы, не очевидные для восприятия, необходимо указывать подробные комментарии не только к заголовку функции, но и самому алгоритму с пояснением каждого шага выполнения алгоритма;

  • В случае внесения изменений в критические участки кода, ядро системы, либо когда сложно проследить последствия, которые может повлечь такое изменение, необходимо указывать подробный комментарий о том кто внес изменение, когда и по какой причине;

  • В том случае если необходимо временно добавить заплатку, без которой система не может работать, но заплатку в дальнейшем планируется убрать необходимо добавлять ключевое слово “//TODO: ”, после которого указывается когда и что должно быть исправлено. Кроме этого необходимо указывать подробный комментарий о том, для чего предназначено временное исправление, кто его внес и когда;

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



Конфигурация



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



Пример:
       <!--Connection strings -->
       
        <add key="MainDatabaseConnectionString" value="server=...;Integrated Security=SSPI;"/>
       
        <add key=" SupportDatabaseConnectionString" value="server=...;Integrated Security=SSPI;"/>
      
<!-- /Connection strings -->


  • Для ключей, хранящих boolean значения, value может быть равно только true или false, а не 0/1 или yes/no.


Переменные и типы



  • Свойства необходимо использовать только тогда, когда это имеет смысл. Если при получении и сохранении значений никакая дополнительная логика не участвует вместо свойства необходимо использовать поле (исключение когда класс bindится на asp.net страницах, т.к. стандартный механизм bindа имеет доступ только к public свойствам);

  • Необходимо использовать максимально простые типы данных. Так, например, необходимо использовать int, а не long, если известно, что для хранимых значений будет достаточно типа int;

  • Константы необходимо использовать только для простых типов данных;

  • Для сложных типов вместо констант необходимо использовать readonly поля;

  • Boxing и unboxing value типов необходимо использовать только когда это действительно необходимо;

  • При задании значений нецелых типов, значения должны содержать как минимум одну цифру до точки и одну после;

  • Необходимо использовать именования типов C#, а не .NET common type system (CTS).
Пример:        int userID = -1; , а не Int32 userID=-1;


  • Модификаторы доступа необходимо указывать всегда. Не смотря на то, что по умолчанию назначается модификатор доступа private, поле модификатора не остается пустым модификатор private необходимо указывать явным образом.

Пример:        private User CreateUser(string firstName, string lastName);



  • Модификаторы доступа (private, protected, internal и public) необходимо указывать в зависимости от того, где требуется доступность соответствующего поля, свойства, функции, класса или конструктора. Модификатор public необходимо указывать только тогда, когда необходим доступ к полю из других проектов, internal когда необходим доступ из других классов внутри одного проекта, protected для предоставления доступа классам потомкам, во всех остальных случаях используется private, т.е. доступ ограничивается самим классом;

  • Поля и переменные инициализируются при их объявлении, когда это возможно.

Пример:        private int  userID = -1;
       
       private string firstName = “”;
       
       private string lastName = “”;



  • Когда для создания класса необходимо передать параметры, используемые при его инициализации, на конструктор по умолчанию (MyClass() { }) необходимо накладывать модификатор доступа private, чтобы избежать создания клиентами неинициализированного объекта.

Пример:
      
private User()
      
{        }
      
public User(int userID)
      
{        }



  • Вместо использования “magic numbers” для идентификаторов статусов, состояний и т.п. необходимо указывать константы или enumы. Идентификаторы состояний в виде чисел использовать нельзя.

Пример не правильного использования: public GetUserByStatus(int statusID);
Пример правильного использования: public GetUserByStatus(
UserStatus userStatus);



  • Тип object необходимо использовать только когда это действительно необходимо, в большинстве случаев вместо него используются genericи. Вместо Hashtable необходимо использовать Dictionary<>, вместо ArrayList используется List<>;

  • В том случае если в getе или setе какого-либо свойства выполняются сложные вычисления, если операция, выполняемая в get или set является преобразованием, имеет побочный эффект или долго выполняется свойство должно быть заменено функциями;

  • Свойство не должно менять своего значения от вызова к вызову, если состояние объекта не изменяется. Если результат при новом вызове может быть другим при том же состоянии объекта, вместо свойства необходимо использовать функции;

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

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


Функции



  • Функции, возвращающие массив, всегда должны возвращать массив. Если нет данных  функции возвращают пустой массив, но не null. Это же касается коллекций;


  • В функциях никогда нельзя использовать больше 7-ми параметров. Если параметров больше они объединяются в класс.




Управление выполнением программы



  • При использовании foreach по коллекции объектов сама коллекция никогда нельзя модифицировать (новые элементы не добавляются, существующие не удаляются);

  • Если задачу можно решить, используя рекурсию и используя циклы, предпочтение необходимо отдавать использованию циклов. Рекурсия необходимо применять только тогда, когда решение с использованием циклов сложнее, чем при использовании рекурсии;

  • Тернарные операции необходимо использовать только для простых проверок. В тех случаях, когда проверка сложная и включает в себя несколько условий используются if/else;

  • В aspx файлах нельзя использовать код. Т.е. tagи <%= “C# code” %> использовать нельзя. Это связано с тем, что при компиляции такой код не проверяется;

  • Сложные проверки, состоящие из множества условий,  необходимо разбивать на несколько простых. Для сохранения промежуточных результатов необходимо использовать boolean переменные;

  • Классы, реализующие интерфейс IDisposable, создаются в директиве using.

Пример: using(SqlConnection sqlConnection = new SqlConnection) { }.



События, делегаты, потоки



  • Перед вызовом делегатов и событий всегда необходимо выполнять проверку на null;

  • При создании простых eventов необходимо использовать стандартные классы EventHandler и EventArgs;

  • При создании сложных eventов для передачи аргументов необходимо использовать классы потомки EventArgs;

  • Для блокировок при написании многопоточных приложений необходимо использовать оператор lock, а не класс Monitor.



Exceptionы и их обработка



  • Блоки try-catch нельзя использовать для управления ходом работы программы, а только для обработки непредвиденных ошибок;
  • При прокидывании исключений выше по StackTraceу, необходимо использовать оператор “throw;”, а НЕ “throw ex;”;

  • Custom exceptionы необходимо наследовать от класса Exception (а не ApplicationException или какого-то другого);

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

  • Все исключения должны быть записаны в log или показаны пользователю системы. Пустые секции catch использовать нельзя;

  • При записи информации об ошибке, как правило, пишется StackTrace.


Приложение 1: Префиксы, используемые для именования контролов в asp.net.


    • Button btn,
    • CheckBox cb,
    • DropDownList ddl,
    • HiddenField hf,
    • HyperLink hl,
    • Image img,
    • ImageButton ibtn
    • Label l,
    • LinkButton lbtn
    • ListBox lb,
    • Literal lt,
    • Panel pnl
    • PlaceHolder ph,
    • RadioButton rb,
    • TextBox tb,
    • Table tbl,
    • Validator val,
    • ValidationSummary vals,

    • AdRotator ar,
    • BulletList bl,
    • Calendar cld,
    • CheckBoxList cbl,
    • FileUpload fup,
    • ImageMap im,
    • Localize loc,
    • MultiView mv,
    • RadioButtonList rbl,
    • Substitution sbs
    • View v,
    • Wizard wiz,
    • Xml xml.


Приложение 2: Сводная таблица правил именования



Идентификатор Регистр Пример
Класс Pascal User
Локальная переменная Camel

user
Интерфейс Pascal IDisposable
Generic Pascal T, TKey, TValue
Public функция Pascal Authenticate
Private функция Pascal Authenticate
Параметр функции

Camel userID
Public свойство Pascal FirstName

Private свойство Pascal FirstName
Public поле Pascal FirstName
Private поле Camel firstName
Enum Pascal UserStatus
Значение enumа

Pascal Active
Exception Pascal UserAuthenticationException

Event Pascal StatusChanged
Namespace Pascal UserManager






Мой сайт - www.msmirnov.ru