C#. Сортировка членов типа с помощью Resharper

от автора

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

Проблема

Иногда при быстром кодинге, мы забываем о сортировке членов и получается что-то вроде этого.

Каша

        private bool fieldBool;          public override int GetHashCode()         {             return base.GetHashCode();         }          public string SomeProperty { get; set; }          public string SecondProperty { get; set; }          public void DoSomething()         {         }          private string fieldString;          public DemoClass(bool fieldBool, string fieldString)         {             this.fieldBool = fieldBool;             this.fieldString = fieldString;         }          public static void DoSomethingStatic()         {         }          public bool Equals(DemoClass other)         {             throw new NotImplementedException();         } 

Сортировка по умолчанию

Но, к счастью, в Resharper есть инструмент Cleanup Code. И если запустить его со включенной опцией Member Reordering, мы получим класс с упорядоченными членами.

После

    public class DemoClass : IEquatable<DemoClass>     {         private bool fieldBool;         private string fieldString;          public DemoClass(bool fieldBool, string fieldString)         {             this.fieldBool = fieldBool;             this.fieldString = fieldString;         }          public string SomeProperty { get; set; }          public string SecondProperty { get; set; }          public bool Equals(DemoClass other)         {             throw new NotImplementedException();         }          public override int GetHashCode()         {             return base.GetHashCode();         }          public void DoSomething()         {         }          public static void DoSomethingStatic()         {         }     } 

Настраиваемые правила

Но что если сортировка по умолчанию нас не устраивает, и отличается от той, которая принята у вас в качестве стандарта?
В этом случае Resharper позволяет переопределить правила используемые по умолчанию.
Идем в ReSharper -> Options -> Code Editing -> C# -> Type Member Layout и включаем Custom Layout.
Перед нами откроется XML который описывает порядок сортировки.

Пример

<Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">   <Pattern>     <!--static fields and constants-->     <Entry>       <Match>         <Or Weight="100">           <Kind Is="constant"/>           <And>             <Kind Is="field"/>             <Static/>           </And>         </Or>       </Match>       <Sort>         <Kind Order="constant field"/>       </Sort>     </Entry>     <Entry>       ...     </Entry>   </Pattern>   <Pattern>     ...   <Pattern> </Patterns> 

Элемент Patterns родительский элемент, может содержать внутри себя множество элементов Pattern, который, в свою очередь, может содержать множество элементов Entry.
Элемент Entry представляет собой запись с которой мы будем осуществлять какие-то действия. Порядок Entry в элементе Pattern влияет на порядок элементов.
Предположим нам нужен следующий порядок: конструкторы, методы, остальные члены.
Для этого мы можем написать такое правило:

Пример

<?xml version="1.0" encoding="utf-8"?>  <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">   <Pattern>     <Entry>       <Match>         <Kind Is="constructor" />       </Match>     </Entry>     <Entry>       <Match>         <Kind Is="method" />       </Match>     </Entry>     <Entry />   </Pattern> </Patterns> 

Каждый элемент Entry содержит вложенный элемент Match, в котором мы описываем, что мы ищем. Мы можем задать более подробные правила для поиска, и пользоваться логическими операторами Or, And, Not и операндами:

<Kind Is="…"/> Аттрибут Is может быть class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member
<Name Is="…" [IgnoreCase=«true/false»] /> Аттрибут Is может содержать регулярное выражение
<HasAttribute CLRName="…" [Inherit=«true/false»] /> Аттрибут CLRName может содержать регулярное выражение
<Access Is="…"/> Аттрибут Is может быть public, protected, internal, protected-internal, private
<Static/>
<Abstract/>
<Virtual/>
<Override/>
<Sealed/>
<Readonly/>
<ImplementsInterface CLRName="…"/> Аттрибут CLRName может содержать регулярное выражение
<HandlesEvent />

Так же, каждый элемент Entry может содержать Sort, что влияет на сортировку найденных членов в пределах одного Entry.
Например, нам надо найти статические поля и константы и расположить их в самом начале класса. А так же отсортировать их по имени. Для этого нам надо добавить следующую запись в начало паттерна.

Пример

    <Entry>       <Match>         <Or>           <Kind Is="constant" />           <And>             <Kind Is="field" />             <Static />           </And>         </Or>       </Match>       <Sort>         <Name />       </Sort>     </Entry> 

Группировка

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

Пример

    <Entry>       <Match>         <And>           <Kind Is="method" />           <Static/>         </And>       </Match>       <Group Region="Static methods" />     </Entry>     <Entry>       <Match>         <Kind Is="method" />       </Match>       <Group Region="Instance methods" />     </Entry> 

Что если, все члены интерфейсов, которые реализовал класс, необходимо отсортировать по имени интерфейса и обернуть в регион с именем этого интерфейса? Для этого мы могли бы сделать так:

Пример

    <Entry>       <Match>         <Kind Is="member" />         <ImplementsInterface/>       </Match>       <Sort>         <ImplementsInterface/>       </Sort>       <Group>         <ImplementsInterface Region="${ImplementsInterface} Members" />       </Group>     </Entry> 

В этом примере, переменная ${ImplementsInterface} будет равна названию интерфейса.
Также, элемент Pattern поддерживает аттрибут RemoveAllRegions, который может быть установлен в true или false (по умолчанию false). Если он установлен в true, то при группировке, будут удалены все регионы которые были до нее.

Вес

Что если условию (Match) соотвествуют сразу несколько типов?
Предположим мы хотим сгруппировать все приватные члены в один регион, кроме методов. Мы могли бы написать следующее правило:

Пример

    <Entry>       <Match>         <Access Is="private" />       </Match>       <Group Region="Private members" />     </Entry>     <Entry>       <Match>         <Kind Is="method" />             </Match>       <Group Region="All methods" />     </Entry> 

Но, если у нас есть приватный метод, то он попадает под оба правила. Первое правило применится раньше, второе условие не будет выполнено. И в итоге, регион который будет содержать приватные члены, будет так же содержать и приватные методы.
Каждый успешно выполненный операнд дает 1 очко веса по умолчанию и эти очки суммируются. В данном случае, в каждом из элементов по одному операнду: <Access Is=«private» /> и <Kind Is=«method» /> соотвественно. Если мы хотим, чтобы второе правило выполнилось, мы можем использовать аттрибут Weight на операнде.

Пример

    <Entry>       <Match>         <Access Is="private" />       </Match>       <Group Region="Private members" />     </Entry>     <Entry>       <Match>         <Kind Is="method" Weight="5"/>             </Match>       <Group Region="All methods" />     </Entry> 

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

Паттерны

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

Пример

  <Pattern>       <Match>         <Kind Is="interface" />       </Match>       ...   </Pattern> 

Все правила, которые будут описаны в приведенном паттерне, будут влиять только на интерфейсы.

Заключение

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

ссылка на оригинал статьи http://habrahabr.ru/post/208464/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *