...

среда, 8 января 2014 г.

[Из песочницы] 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 очко веса по умолчанию и эти очки суммируются. В данном случае, в каждом из элементов по одному операнду: и соотвественно. Если мы хотим, чтобы второе правило выполнилось, мы можем использовать аттрибут 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, которые, возможно сделают вашу жизнь легче.

Этот пост не претендует на исчерпывающую документацию по данному вопросу.

Источники:

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий