Для загрузки Стандарты и правила доступны по следующей ссылке на моем сайте: 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 и сокращение стоит в начале имени идентификатора. В этом случае сокращение пишется в нижнем регистре.
Пример:
PPCAccount (PPC – сокращение от pay per click) для pascal case,
ppcAccount для camel case.
PPCAccount (PPC – сокращение от pay per click) для pascal case,
ppcAccount для camel case.
Использование верхнего и нижнего регистра в именах
- Запрещается создавать два различных namespace’а, функции, типа или свойства с одинаковыми именами, отличающиеся только регистром. Запрещается создавать функции с именами параметров, отличающимися только регистром. Ниже приведены примеры НЕправильных названий.
Пример:
KeywordManager и Keywordmanager;
KeywordManager.Keyword и KeywordManager.KEYWORD;
int id {get, set} и int ID {get, set};
findByID(int id) и FindByID(int id);
void MyFunction(string s, string S).
KeywordManager и Keywordmanager;
KeywordManager.Keyword и KeywordManager.KEYWORD;
int id {get, set} и int ID {get, set};
findByID(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,
потомок FigureCircle;
базовый класс Figure,
потомок FigureCircle;
- Имена файлов, в которых находятся классы, совпадают с именами классов. Для именования файлов используется стиль 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.
основная функция – 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).
public void EncodeString(string sourceString, ref string encodedString),
а не public void EncodeString(string string1, ref string string2).
- В том случае, когда это не препятствует понимаю кода, в качестве имени параметра функции используется имя соответствующего параметру класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.
Пример:
UserFactory.Create(Company company);
CheckUsers(UserCollection users);
UserFactory.Create(Company company);
CheckUsers(UserCollection users);
- Имена параметров не должны совпадать с именами членов класса, если этого не удается избежать, то для разрешения конфликтов используется ключевое слово this.
Пример:
public void CreateUser(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
{
this.firstName = firstName;
this.lastName = lastName;
}
- В именах параметров не используется венгерская нотация.
Правила именования свойств
- Для именования свойств используется стиль pascal case;
- Свойства объявляются согласно следующему шаблону:
<Модификатор доступа> [Другие модификаторы] <Тип> <Название свойства>;
Пример: public static User CurrentUser { get; }
- В том случае, когда это не препятствует понимаю кода, в качестве имени свойства используется имя соответствующего свойству класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.
Пример:
public User User { get; set; }
public UserCollection Users { get; set; }
public User User { get; set; }
public UserCollection Users { get; set; }
- Название свойства типа bool должно представлять из себя вопрос, требующий ответа да или нет.
Примеры названий: “CanDownload”, “HasKeywords”, “IsChecked”, “NeedsUpdate”.
Правила именования полей
- Для именования полей, доступных вне класса, используется стиль pascal case, для private полей - camel case;
- Поля объявляются согласно следующему шаблону:
<Модификатор доступа> [Другие модификаторы] <Тип> <Название поля>;
Пример: private static User currentUser = null;
- В том случае, когда это не препятствует понимаю кода, в качестве имени поля используется имя соответствующего полю класса. Для коллекций и массивов используется имя объектов, содержащихся в коллекции или массиве.
Пример:
public User User = new User();
public UserCollection Users = new UserCollection();
public User User = new User();
public UserCollection Users = 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; }
private User user;
public User User { get; set; }
- Если set или get свойства состоит из одной операции – весь set или get размещается на одной строке.
Пример:
Public User
{
get { return user; }
}
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 -->
<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 = “”;
private string firstName = “”;
private string lastName = “”;
- Когда для создания класса необходимо передать параметры, используемые при его инициализации, на конструктор по умолчанию (MyClass() { }) необходимо накладывать модификатор доступа private, чтобы избежать создания клиентами неинициализированного объекта.
Пример:
private User()
{ }
public User(int userID)
{ }
private User()
{ }
public User(int userID)
{ }
- Вместо использования “magic numbers” для идентификаторов статусов, состояний и т.п. необходимо указывать константы или enum’ы. Идентификаторы состояний в виде чисел использовать нельзя.
Пример не правильного использования: public GetUserByStatus(int statusID);
Пример правильного использования: public GetUserByStatus(UserStatus userStatus);
Пример правильного использования: 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 |
Очень полезно.
ОтветитьУдалитьСпасибо.
А почему не используете для полей _ перед названием?
Так бы их было проще отличить от входных параметров.
И ещё, в C# нету функци, нету процедур, а есть методы. Вроде всё гуд описано, но как прочитал, что методы называете функциями, появились сомнения... А так в основном всё, что описано сам использую предварительно не читав соглашений. Просто сразу делал как в .NET, правда иногда параметры в методах именовал с большой буквы. Короче мне понравилось, но "функции"...
ОтветитьУдалитьМетод, взвращающий значение - это и есть функци.
УдалитьАвтор, скорее всего, намеренно так назвал их по отдельности, а не общим определением "метод", чтобы было легко понять о чем речь без дополнительных объяснений. Вобщем, не увидел в этом большой проблемы.