Соглашения по оформлению кода команды RSDN

Автор: RSDN Team
Источник: RSDN Magazine #1-2004
Опубликовано: 27.02.2003
Версия текста: 1.0.1
Введение
Список терминов
Именование идентификаторов
Общие правила
Пространства имен
Типы
Элементы типов (type members)
Классы/Структуры
Интерфейсы
Атрибуты
Перечисления
Поля
Методы
Свойства
События
Параметры
Стиль кода
Оформление
Локальные переменные
Комментарии
Объявления и определения
Инструкции (statements)
Общие правила разработки классов

Введение

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

Примеры внутри этого документа даются на языке C#, и вообще, все соглашения в первую очередь ориентированны на разработку, ведущуюся с использованием C#, но эти правила применимы и к другим языка (конечно, с учетом их специфики).

Данные правила являются плодом коллективного труда команды RSDN, но мы позволяем использовать их кому угодно. :) При перепечатке или использовании этих правил в качестве основы для ваших собственных сделайте ссылку на этот документ.

Список терминов

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

Именование идентификаторов

Общие правила

  1. Помните! Код чаще читается, чем пишется, поэтому не экономьте на понятности и чистоте кода ради скорости набора.
  2. Не используйте малопонятные префиксы или суффиксы (например, венгерскую нотацию), современные языки и средства разработки позволяют контролировать типы данных на этапе разработки и сборки.
  3. Не используйте подчеркивание для отделения слов внутри идентификаторов, это удлиняет идентификаторы и затрудняет чтение. Вместо этого используйте стиль именования Кемел или Паскаль.
  4. Старайтесь не использовать сокращения лишний раз, помните о тех, кто читает код.
  5. Старайтесь делать имена идентификаторов как можно короче (но не в ущерб читабельности). Помните, что современные языки позволяют формировать имя из пространств имен и типов. Главное, чтобы смысл идентификатора был понятен в используемом контексте. Например, количество элементов коллекции лучше назвать Count, а не CountOfElementsInMyCollection.
  6. Когда придумываете название для нового, общедоступного (public) класса, пространства имен или интерфейса, старайтесь не использовать имена, потенциально или явно конфликтующие со стандартными идентификаторами.
  7. Предпочтительно использовать имена, которые ясно и четко описывают предназначение и/или смысл сущности.
  8. Старайтесь не использовать для разных сущностей имена, отличающиеся только регистром букв. Разрабатываемые вами компоненты могут быть использованы из языков, не различающих регистр, и некоторые методы (или даже весь компонент) окажутся недоступными.
  9. Старайтесь использовать имена с простым написанием. Их легче читать и набирать. Избегайте (в разумных пределах) использования слов с двойными буквами, сложным чередованием согласных. Прежде, чем остановиться в выборе имени, убедитесь, что оно легко пишется и однозначно воспринимается на слух. Если оно с трудом читается, и вы ошибаетесь при его наборе, возможно, стоит выбрать другое.

Стили использования регистра букв

Сокращения

  1. Не используйте аббревиатуры или неполные слова в идентификаторах, если только они не являются общепринятыми. Например, пишите GetWindow, а не GetWin.
  2. Не используйте акронимы, если они не общеприняты в области информационных технологий.
  3. Широко распространенные акронимы используйте для замены длинных фраз. Например, UI вместо User Interface или Olap вместо On-line Analytical Processing.
  4. Если имеется идентификатор длиной менее трех букв, являющийся сокращением, то его записывают заглавными буквами, например System.IO, System.Web.UI. Имена длиннее двух букв записывайте в стиле Паскаль или Кэмел, например Guid, Xml, xmlDocument.

Примеры:

using System.IO;
using System.Web.UI;

public class Math
{
    public const PI = ...;
    public const E  = ...;
}

Выбор слов

  1. Не используйте имена, совпадающие с глобальными пространствами имен, такими, как System и Microsoft. Старайтесь по возможности избегать совпадения с названиями других сущностей .NET Framework.
  2. Ниже приводится список имен, использование которых нежелательно:
AddHandlerAddressOfAliasAndAnsi
AsAssemblyAutoBaseBoolean
ByRefByteByValCallCase
CatchCBoolCByteCCharCDate
CDecCDblCharCIntClass
CLngCObjConstCShortCSng
CStrCTypeDateDecimalDeclare
DefaultDelegateDimDoDouble
EachElseElseIfEndEnum
EraseErrorEventExitExternalSource
FalseFinalizeFinallyFloatFor
FriendFunctionGetGetTypeGoto
HandlesIfImplementsImportsIn
InheritsIntegerInterfaceIsLet
LibLikeLongLoopMe
ModModuleMustInheritMustOverrideMyBase
MyClassNamespaceNewNextNot
NothingNotInheritableNotOverridableObjectOn
OptionOptionalOrOverloadsOverridable
OverridesParamArrayPreservePrivateProperty
ProtectedPublicRaiseEventReadOnlyReDim
RegionREMRemoveHandlerResumeReturn
SelectSetShadowsSharedShort
SingleStaticStepStopString
StructureSubSyncLockThenThrow
ToTrueTryTypeOfUnicode
UntilvolatileWhenWhileWith
WithEventsWriteOnlyXorevalextends
instanceofpackagevar

Пространства имен

  1. Для пространства имен используйте имя компании, затем название продукта и, возможно, название подсистемы или существенной части проекта. Например, Rsdn.Framework, Rsdn.MegaTool.Scanner.
  2. Используйте стиль Паскаль для регистра букв.
  3. При объявлении пространства имен используйте единственную директиву namespace с полным именем пространства имен. Не используйте вложенные объявления пространств имен. Это замечание не относится к С++/MС++, так как в них нет отличной от вложенных пространств имен возможности создавать составные пространства имен.
  4. Множественное число следует использовать в случае, если пространство имен объединяет некоторое количество разных, но семантически похожих сущностей. И наоборот, когда пространство имен содержит некую подсистему, стоит использовать единственное число. Сравните: Rsdn.Collections, но не Rsdn.Collection; Rsdn.Controls, но не Rsdn.Control; Rsdn.Network, но не Rsdn.Networks; Rsdn.Web, но не Rsdn.Webs;
  5. Не используйте одно и то же имя для класса и пространства имен. Например, не используйте класс Debug и пространство имен Debug.

Примеры:

namespace Rsdn.Controls {}
namespace Rsdn.Collections {}
namespace Rsdn.Common {}
namespace Rsdn.Network {}

Импорт пространств имен (директива using)

  1. Перечислять импортируемые пространства имен необходимо в следующей последовательности: пространства имен .NET Framework, пространства имен сторонних производителей, собственные пространства имен из других проектов, пространства имен из текущего проекта. Каждый такой раздел должен отделяться одной пустой строкой, а имена внутри раздела должны быть отсортированы по алфавиту.
  2. Директивы using, содержащие alias, должны идти в конце соответствующих разделов, и также быть упорядоченными по алфавиту.

Типы

  1. Все не вложенные типы (размещаемые в пространствах имен или непосредственно в глобальном пространстве), за исключением делегатов, должны находиться в отдельных файлах.
  2. Старайтесь объявлять вложенные типы в начале внешнего типа.
  3. Старайтесь не делать излишней вложенности типов. Помните, что вложенными должны быть только тесно связанные типы.

Элементы типов (type members)

  1. Элементы типов должны отделяться одной строкой друг от друга. Вложенные типы должны отделяться двумя строками. При объявлении большого количества полей, используемых внутри класса (не публичных), можно опускать пустую строку (особенно если поля отделены XML-комментариями).

Классы/Структуры

  1. Используйте существительное (одно или несколько прилагательных и существительное) для имени класса.
  2. Используйте стиль Паскаль для регистра букв.
  3. Не используйте специальных префиксов, поясняющих, что это класс. Например, FileStream, а не CFileStream.
  4. В подходящих случаях используйте составные слова для производных классов, где вторая часть слова поясняет базовый класс. К примеру, ApplicationException – вполне подходящее название для класса, унаследованного от Exception, поскольку ApplicationException является наследником класса Exception. Не стоит, однако злоупотреблять этим методом, пользуйтесь им разумно. К примеру, Button – вполне подходящее название для класса, производного от Control. Общее правило может быть, например, таким: «Если производный класс незначительно меняет свойства, поведение или внешний вид базового, используйте составные слова. Если класс значительно расширяет или меняет поведение базового, используйте новое существительное, отражающее суть производного класса». LinkLabel незначительно меняет внешний вид и поведение Label и, соответственно, использует составное имя.
  5. Используйте составное имя, когда класс принадлежит некоторой специфичной категории, например FileStream, StringCollection, IntegrityException. Это относится к классам, которые являются потоками (Stream), коллекциями (Collection, Queue, Stack), ассоциативными контейнерами (Dictionary), исключениями (Exception).
  6. Для классов-наследников, реализующих интерфейс IDictionary рекомендуется использовать тройное имя в виде <ТипКлюча>To<ТипЗначения>Dictionary. Вместо Dictionary можно использовать слово Map. Если это очевидно, можно опускать название значения. Примеры: StringToStringDictionary, StringToIntegerMap или KeywordMap. Переменным такого типа рекомендуется давать более конкретное семантическое название, например userToPasswordMap (user --> password), nameServiceDictionary (name --> service).
  7. Для базового класса, предназначенного не для прямого использования, а для наследования, следует использовать суффикс Base. Например, CollectionBase. Такие классы также стоит делать абстрактными.
  8. Коллекциям (реализующим интерфейс ICollection/IList) нужно давать имя в виде <ИмяЭлемента>Collection. Переменным же этих типов лучше давать имена, являющиеся множественным числом от элемента. Например, коллекция кнопок должна иметь имя ButtonCollection, а переменная buttons.

Примеры:

public class FileStream {}
public class Button {}
public class String {}
public class StringCollection {}

Интерфейсы

  1. Используйте описывающее существительное, прилагательное или одно или несколько прилагательных и существительное для идентификатора интерфейса. Например, IComponent – это описывающее существительное, ICustomAttributeProvider – это конкретизированное прилагательными существительное, а IPersistable – это характеризующее прилагательное.
  2. Используйте стиль Паскаль для регистра букв.
  3. Используйте префикс I (заглавная i) для интерфейсов, чтобы уточнить, что тип является интерфейсом. Старайтесь избегать интерфейсов с двумя I в начале, например IIdentifiable. Попробуйте подобрать синоним, например IRecognizable.
  4. Для пары класс-интерфейс, в которой класс является некоторой стандартной или эталонной реализацией интерфейса, используйте одинаковые имена, отличающиеся только префиксом I для интерфейса. Например, IConfigurationManager и ConfigurationManager.

Примеры:

public interface IServiceProvider {}
public interface IFormatable {}

public interface IComponent 
{
    // Объявление интерфейса
}

public class Component : IComponent // Стандартная реализация интерфейса
{
    // Реализация
}

Атрибуты

  1. Класс, являющийся атрибутом, должен иметь суффикс Attribute. Ни один класс, атрибутом не являющийся, не должен иметь такого суффикса. Если семантика класса требует в названии слова что-то вроде Attribute, используйте синонимы, например Descriptor, Sign, Qualifier, Specifier, Declarator.

Пример:

public class ObsoleteAttribute {};

Перечисления

  1. Используйте стиль Паскаль для регистра букв в названии и значениях перечисления.
  2. Не используйте суффикс Enum в названии типа, вместо этого используйте более конкретный суффикс, например Style, Type, Mode, State. Чтобы код легче читался, используйте следующее правило: «Название перечисления + is + значение должно образовывать простое предложение». Например: BorderStyle.Single ( Border style is single, ThreadState.Aborted --> Thread state is “aborted”.
  3. Если перечисление обладает атрибутом [Flags], используйте множественное число или суффикс Flags.
  4. Записывайте значения перечисления на отдельных строках. Если элементы имеют определенный семантический порядок, описывайте их именно так, иначе используйте алфавитный порядок.
  5. Если значение одного из членов перечисления зависит от других, используйте булевы операции (&, |, ^), а не литеральные константы.
  6. Имена членов перечисления не должны содержать имени перечисления и другой не относящейся к конкретному значению информации.

Пример:

public enum CodeStyle
{
  Good,
  Normal,
  Bad,
  Ugly
}

Поля

  1. Непубличные поля (private, protected и protected internal) именуются в стиле Кэмел и начинаются с префикса _.
  2. Публичные поля именуются в соответствии с правилами именования свойств.
  3. Одна декларация должна содержать не более одного поля и должна располагаться на одной строке.
class A
{
    // Так нельзя:
    int _var1, _var2; 

    // Так тоже:
    int _var1, 
        _var2; 

    // Надо так:
    int _var1;
    int _var2;
    ...
  1. Публичные поля должны в обязательном порядке документироваться XML-комментариями. Желательно снабжать XML-комментариями и непубличные поля.
  2. Обращаясь к публичным полям старайтесь избегать их передачи по ссылке, т.к. велика вероятность того, что в следующих версиях приложения эти поля могут стать свойствами.

Методы

  1. Используйте глаголы или комбинацию глагола и существительных и прилагательных для имен методов.
  2. Используйте стиль Паскаль для регистра букв (вне зависимости от области видимости метода).

Примеры:

private int RemoveAll() {}
public void GetCharArray() {}
internal static Invoke() {}

Свойства

  1. Используйте существительное или одно или несколько прилагательных и существительное для имени свойства.
  2. Используйте стиль Паскаль для регистра букв.
  3. В подходящих случаях используйте имя свойства, совпадающее с типом его значения. Одним из критериев для применения этого правила является наличие единственного свойства со значением некоторого (нетривиального) типа.
  4. Старайтесь избегать использования имен свойств, совпадающих с названиями каких-либо типов, если значения этих свойств не являются значениями этих типов. В этом случае будет трудно получить доступ к статическим членам типа или значениям перечисления. Например, при использовании конструкции public int Color { get; set; }, обращение Color.Xxx будет рассматриваться как получение свойства Color и затем доступ к свойствам или методам этого значения, которое в данном случае будет являться типа System.Int32.
  5. Рассмотрите возможность включения имени типа в идентификатор свойства, особенно если этот тип – перечисление. Например, OuterBorderStyle, BackColor, SocketFlags.

Примеры:

public class SampleClass
{
    public Color BackColor 
    {
        // Code for Get and Set accessors goes here.
    }
}

public class Control 
{
    public Color Color 
    { 
        get {/* Insert code here. */} 
        set {/* Insert code here. */} 
    }
}

События

  1. Используйте суффикс EventHandler для делегатов, являющихся типами событий. Другие классы не должны использовать этот суффикс.
  2. Создавая событие в компонентах и control-ах, старайтесь описывать их по следующей схеме. Определите два параметра с именами sender и e. Параметр sender описывает объект, инициировавший событие, и всегда должен быть типа object, даже если возможно использование более конкретного типа. Второй параметр, e, должен содержать состояние и дополнительную информацию, соответствующую событию. Этот параметр должен быть конкретного типа, относящегося к событию.
  3. Делайте тип, описывающий связанную с событием информацию, производным от EventArgs, и используйте суффикс EventArgs. Другие классы, не описывающие информацию о событии, не должны использовать этот суффикс.
  4. Для имен событий старайтесь использовать глаголы, которые описывают производимое над объектом действие (например, Click, GotFocus или FontChanged).
  5. Не используйте суффиксы наподобие On, Before, After для идентификатора события. Используйте соответствующую форму глагола, например Closing перед закрытием и Closed после закрытия.
  6. При описании события также предоставляйте виртуальный protected-метод, который можно переопределить в классе-наследнике. Называйте такой метод OnXxx, где Xxx – имя события. В качестве параметров таких методов не следует передавать sender, так как – это всегда текущий объект (this).
  7. Пытайтесь подобрать стандартный делегат и название для своих событий. Например, если ваш элемент управления должен реагировать на нажатие кнопки мыши, следует использовать стандартное событие Click. Для элементов управления, обычно, такие события уже объявлены в базовом классе Control.

Примеры:

public delegate void MouseEventHandler(object sender, MouseEventArgs e);

public class MouseEventArgs : EventArgs 
{
  private int x;
  private int y;

  public MouseEventArgs(int x, int y) 
  { 
    this.x = x; 
    this.y = y; 
  }

  public int X { get { return x; } } 
  public int Y { get { return y; } } 
}

public class Control
{
  public event MouseEventHandler MouseMove;
  protected virtual void OnMouseMove(MouseEventArgs e)
  {
    if (MouseMove != null)
      MouseMove(this, e);
  }
}

Параметры

  1. Из имени и типа параметра должны быть понятны его назначение и смысл.
  2. Используйте стиль Кэмел для регистра букв в имени параметра.
  3. Старайтесь избегать указания типа в имени параметра.
  4. Не усложняйте прототип метода «зарезервированными» параметрами, которые, возможно, будут использоваться в будущих версиях реализации. Если в будущем понадобится новый параметр, используйте перегрузку методов.
  5. Имена параметров не должны совпадать с именами членов класса. Если этого все же не удается избежать, используйте ключевое слово this для разрешения конфликтов (допустимо различие в регистре или префиксе _ для полей классов).

Примеры:

Type GetType(string typeName);
string Format(string format, params object[] args);

Стиль кода

Оформление

  1. Используйте табуляцию, а не пробелы для отступов. В средах типа VS лучше использовать режим табуляции. Его можно настроить в диалоге Options -> Text Editor -> Ваш_любимый_язык ->Tabs: Indenting = Smart, Tabs = Keep Tabs. В общем, это настройки по умолчанию для многих языков.
  2. При форматировании текста (кроме отступа в начале строки) используйте пробелы. Для этого удобно использовать режим Virtual Space, который в VS 2002 настраивается в Options -> Text Editor -> Ваш_любимый_язык -> General.
  3. Избегайте строк длиннее 78 символов, переносите инструкцию на другую строку при необходимости.
  4. При переносе части кода инструкций и описаний на другую строку вторая и последующая строки должны быть отбиты вправо на один отступ (табуляцию).
  5. Оставляйте запятую на предыдущей строке так же, как вы это делаете в обычных языках (русском, например).
  6. Избегайте лишних скобок, обрамляющих выражения целиком. Лишние скобки усложняют восприятие кода и увеличивают возможность ошибки. Если вы не уверены в приоритете операторов, лучше загляните в соответствующий раздел документации.
  7. Не размещайте несколько инструкций на одной строке. Каждая инструкция должна начинаться с новой строки.

Примеры:

longMethodCall(expr1, expr2, expr3,
    expr4, expr5);

var1 = a * b / (c - g + f)
    + 4 * z; 

var2 = (a * (b 
    * (c + d)
    + e * (f / z))
    + 4);

Пустые строки

  1. Используйте две пустые строки между логическими секциями в исходном файле.
  2. Используйте две пустые строки между объявлениями классов и интерфейсов.
  3. Используйте одну пустую строку между методами.
  4. Если переменные в методе объявляются отдельным блоком, используйте одну пустую строку между их объявлением и инструкцией, идущей за этим блоком.
  5. Используйте одну пустую строку между логическими частями в методе.

Пробелы в строке

  1. После запятой должен быть пробел. После точки с запятой, если она не последняя в строке (напр. в инструкции for), должен быть пробел. Перед запятой или точкой с запятой пробелы не ставятся.
  2. Все операторы должны быть отделены пробелом от операндов с обеих сторон.
  3. Логически связный блок регулярной структуры желательно форматировать в виде таблицы. При этом для выравнивания в таблице следует использовать пробелы, но не табуляцию. Среды типа VS автоматизируют процесс форматирования, вставляя пробелы или табуляции в соотвтествии с пользовательскими настройками. Будьте внимательны и проверяйте конечный результат, включая неотображаемые символы (для VS 2002 и старше – меню Edit->Advanced->View White Space).
ПРИМЕЧАНИЕ

Для упрощения работы можно использовать следующий трюк. Таблицу можно сформировать с помощью табуляции, выделить область прямоугольным выделением (Alt + курсор мыши вправо), а затем применить к этой области Edit->Advanced->Untabify Selection.

Примеры:

TestMethod(a, b, c);
int Bar { get; set; }

a=b;                         // неверно
a = b;                       // верно
for (int i=0; i<10; ++i)     // неверно
{
}
for( int i = 0 ;i< 10;++i)   // неверно

for (int i = 0; i < 10; ++i) // верно 
{                            
}                             

if(a==b){}                   // неверно

if (a == b)                  // верно 
{
}

// табличное форматирование
string name    = "Mr. Ed";
int    myValue = 5;
Test   aTest   = Test.TestYou;

Локальные переменные

  1. Используйте стиль Кэмел для регистра букв в именах переменных.
  2. Объявляйте переменные непосредственно перед их использованием.
  3. Счетчики в циклах традиционно называют i, j, k, l, m, n.
  4. Для временных локальных переменных, используемых в коротких участках кода, можно давать имена, состоящие из начальных букв слов имени типа.
  5. Не объявляйте более одной переменной в одной инструкции.
  6. Имена скрытых (protected, protected internal и private) переменных-членов класса и статических переменных начинайте с одиночного символа подчеркивания.
  7. Имена публичных (internal и public) переменных должны быть в стиле Паскаль и не иметь подчеркивания в качестве префикса.
  8. Комментируйте объявления так, чтобы были понятны назначение и способы использования переменной.
  9. Инициализируйте переменные при объявлении, если это возможно.

Пример:

class Class1
{
    private int _totalLineCount = 0;

    void SomeFunction(int startValue)
    {
        int lineCount = startValue;

        using (StreamReader sr = File.OpenText(@"c:\MyFile.txt"))
        {
            while (sr.ReadLine() != null)
                lineCount++;
        }

        _totalLineCount = lineCount;
    }
}

Комментарии

  1. Не используйте многострочные (/*...*/) комментарии для описания классов и методов, используйте для этих целей XML documentation-комментарии. Многострочные комментарии не могут быть вложенными, поэтому их использование может создать проблемы.
  2. Для описания сути некоторого участка кода, пояснений к алгоритму и другой важной информации используйте несколько подряд идущих однострочных комментариев (//...). Между группой комментариев и собственно кодом поставьте пустую строку. Это покажет, что комментарий относится к блоку кода, а не к конкретной инструкции. Напротив, если комментарий относится к конкретной инструкции, прижмите его вплотную к этой инструкции.
  3. Комментируйте объявления переменных, по возможности используя XML-комментарии. Если язык не поддерживает XML-комментариев, можно использовать однострочные комментарии на той же строке, как это показано ниже.
  4. Отделяйте текст комментария одним пробелом «// Текст комментария.».
  5. Комментируя код, старайтесь объяснять, что он делает, а не какая операция производится. Так, инструкции if соответствует выражение «если... то...», причем часть, идущая за «то», является кодом, который будет выполняться, если выражение в if будет верным. Таким образом, для конструкции «if (somePath && File.Exists(somePath))», нужно написать комментарий «// Если выбранный файл существует, то...», а не «// Производим проверку на наличие файла и, если он имеется, удаляем его». Часть предложения, идущую за «то», вписывайте непосредственно перед выполнением конкретных действий. Для инструкций, осуществляющих действия, пишите «// Производим...» или «// Делаем...», где вместо троеточия вписывайте описания действий. Описывая действия, старайтесь описывать суть происходящего, а не то, что делают те или иные операторы. Так, совершенно бессмысленны комментарии вроде «Присваиваем переменной a значение b» или «вызываем метод f».
  6. Помните, что экономить на комментариях нельзя. Однако не стоит также формально подходить к процессу создания комментариев. Задача комментария – упростить понимание кода. Есть немало случаев, когда сам код отличным образом себя документирует.

Примеры:

/// <summary>indentation level</summary>
int level;
int size;  // size of table

// Line 1
//
ArrayList list = new ArrayList(10);

// Line 1
// Line 2
//
for (int i = 0; i < list.Count; i++)
    ...

Объявления и определения

Объявления классов и интерфейсов

  1. Базовые классы и интерфейсы должны указываться на той же строке, что и объявление класса. Если список не умещается в 78 символов, снесите его, начиная с двоеточия, и сделайте отступ на одну табуляцию вправо. Размещайте на строке столько элементов, сколько умещается в 78 символов.
  2. Открывающая фигурная скобка должна находиться на строке, следующей за объявлением класса, на том же уровне отступа, что и класс.
  3. Закрывающая фигурная скобка должна находиться на отдельной строке на том же уровне, что и класс.
  4. Содержимое класса должно быть сдвинуто на одну табуляцию относительно объявления.

Пример:

class MySample : MyClass, IMyInterface
{
  // содержимое класса
}

Объявления членов класса и интерфейса

  1. Между именем метода и открывающей скобкой списка параметров не должно быть пробела.
  2. Тело метода должно быть сдвинуто на одну табуляцию относительно объявления.
  3. Вид доступа к члену класса всегда должен быть явно указан.
  4. Модификаторы должны идти в следующем порядке: сначала модификаторы доступа, а потом все остальные.
  5. В конструкторе вызов конструктора базового (или этого же) класса (конструкция «: base(...)» «: this(...)») оформляется таким же образом, как список базовых типов у класса.

Пример:

class MySample : MyClass, IMyInterface
{
  private int myint;

  public MySample(int myint)
    : base("hello, MyClass")
  {
    this.myint = myint;
  }

  [MyCustomAttribute]
  private void Inc()
  {
    ++myint;
  }

  protected virtual void EmptyVirtualMethod()
  {
  }
}

Инструкции (statements)

  1. Каждая инструкция должна располагаться на отдельной строке.
  2. Составные инструкции оформляются открывающей фигурной скобкой на отдельной строке, списком инструкций, сдвинутым на одну табуляцию, и закрывающей фигурной скобкой на отдельной строке.
  3. После ключевого слова (напр. if, while, for) перед открывающей круглой скобкой (если она есть) должен быть пробел.

Оформление If, if-else, if else-if else

if:

if (condition)
{
    DoSomething();
    ...
}

if (condition)
    DoSomething();

// Неверно:
if(condition) DoSomething();

if (condition) {
    DoSomething();
}

if (condition)
    {
    DoSomething();
    }

if с последующим else:

if (condition) 
{
    DoSomething();
    ...
} 
else 
{
    DoSomethingOther();
    ...
}

if (condition) 
    DoSomething();
else 
    DoSomethingOther();

// Неверно:
if (condition) {
    DoSomething();
    ...
} else {
    DoSomethingOther();
    ...
}

if (condition) DoSomething(); else DoSomethingOther();

if-else:

if (condition) 
{
    DoSomething();
    ...
} 
else if (condition) 
{
    DoSomethingOther();
    ...
} 
else 
{
    DoSomethingOtherAgain();
    ...
}

Оформление for, foreach

for:

for (int i = 0; i < 5; ++i) 
{
    ...
}

for с пустым телом:

for (initialization; condition; update)
    ;

foreach:

foreach (int i in IntList) 
{
    ...
}

Оформление while, do-while

while:

while (condition) 
{
    ...
}

while с пустым телом:

while (condition)
    ;

do-while:

do 
{
    ...
}
while (condition);

Оформление switch

switch с длинными последовательностями в case:

switch (condition) 
{
    case 1:
    case 2:
        x = ...;
        break;
    case 3:
        x = ...;
        break;
    default:
        x = ...;
        break;
}

switch с короткими последовательностями в case:

switch (condition) 
{
    case 1:  x = 1;   break;
    case 2:  x = 2;   break;
    case 3:  x = 3;   break;
    default: x = 100; break;
}

Оформление try-catch

try-catch без конкретного исключения:

try 
{
    ...
}
catch 
{
...
}

try-catch с фильтром исключений:

try 
{
    ...
}
catch (Exception ex)
{
    ...
}

try-catch с фильтром исключений и блоком finally:

try 
{
    ...
}
catch (SomeException ex) 
{
    ...
}
catch (Exception ex) 
{
    ...
}
finally 
{
    ...
}

Оформление инструкции using

Одиночный using:

using (initialization)
{
    ...
}

Вложенные using:

using (initialization1)
using (initialization2)
{
    ...
}

Общие правила разработки классов

  1. Допускается определять в классе публичные (public и internal) поля, но они должны однозначно интерпретироваться как lightweight-поля. Оформляйте их стилем Паскаль и указывайте в документации, что не следует передавать такие поля по ссылке. Старайтесь (если, конечно, на то нет особых причин) избегать смешивания в одном классе/структуре публичных полей и свойств.
  2. Старайтесь реализовать в виде свойств только то, что отражает состояние класса или объекта. Например, если вы делаете свою коллекцию, то количество элементов (Count) должно быть свойством, а операцию преобразования ее в массив (GetArray) лучше сделать методом. Причем вместо Get в данном случае лучше использовать другое слово, например, ToXxxx. То есть метод будет называться ToArray().
  3. Используйте методы, если выполняемая операция является преобразованием, имеет побочный эффект или долго выполняется.
  4. Используйте метод, если важен порядок выполнения операций.
  5. Свойство не должно менять своего значения от вызова к вызову, если состояние объекта не изменяется. Если результат при новом вызове может быть другим при том же состоянии объекта, используйте метод.
  6. Не используйте свойства «только для записи». Потребность в таком свойстве может быть признаком плохого проектирования.
  7. Запечатанные (sealed) классы не должны иметь ни защищенных, ни виртуальных методов. Такие методы используются в производных классах, а sealed-класс не может быть базовым.
  8. Классы, реализующие интерфейс IDisposable, ни в коем случае не должны передаваться по значению.
  9. Классы, определяющие только статические методы и свойства, не должны иметь открытых или защищенных конструкторов, поскольку никогда не нужно создавать их экземпляры.
  10. Не используйте литеральные константы (магические числа, зашитые в код размеры буферов, времена ожидания и тому подобное). Лучше определите константу (если вы никогда не будете ее менять) или переменную только для чтения (если она может измениться в будущих версиях вашего класса).
  11. Старайтесь обрабатывать только известные вам исключения. Если вы все же обрабатываете все исключения, то или, проведя необходимую обработку, генерируйте исключение повторно, чтобы его могли обработать последующие фильтры, или выводите пользователю максимально полную информацию об ошибке. Если целью перехвата исключений является очистка ресурсов после сбоя, лучше воспользоваться секцией finally.


Эта статья опубликована в журнале RSDN Magazine . Информацию о журнале можно найти здесь